第1回 明瞭なコーディングのために:特集:C# 7の新機能詳説(2/3 ページ)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは簡潔で分かりやすいコードを記述するために使える。
ローカル関数
ローカル関数とは、メソッドなどの中に書くメソッドだ。メソッドにアクセスできるスコープを限定できる。
メソッドのスコープを限定する
メソッドのスコープはprivate修飾子を付けると(=既定では)クラス内部になるが、もっとスコープを狭くしたいこともある。例えば次のコードのIsMultipleOf3メソッド/IsMultipleOf5メソッドは、FizzBuzz1メソッドから呼び出しているだけだ。そのスコープをFizzBuzz1メソッド内部だけに限定できれば、コードはより明確になるだろう。
public static string FizzBuzz1(int n)
{
if (IsMultipleOf3(n) && IsMultipleOf5(n))
return "Fizz Buzz";
if (IsMultipleOf3(n))
return "Fizz";
if (IsMultipleOf5(n))
return "Buzz";
return n.ToString();
}
// 以下のメソッドのスコープは?(クラス内全体でよいだろうか?)
private static bool IsMultipleOf3(int n) => (n % 3) == 0;
private static bool IsMultipleOf5(int n) => (n % 5) == 0;
IsMultipleOf3メソッド/IsMultipleOf5メソッドのスコープは、このコードを含むクラスの全体である。しかし、実際に呼び出しているのはFizzBuzz1メソッドだけなので、スコープはFizzBuzz1メソッド内部だけで十分だ。
このような状況は、例えばコードを明瞭にするためにExtract Methodリファクタリングを行うと発生する。もちろん、リファクタリングで切り出したメソッドがクラスの他の場所からも利用されると分かっているなら(そのように予想されるなら)、そのままでよい。そうでなければ、切り出したメソッドのスコープを切り詰めたくなるはずだ。上のコード例でいうと、FizzBuzz1メソッドが専用に使っているメソッドだということを、明確にしておきたいのである。
C# 7で導入されたローカル関数では、そのスコープはローカル関数を置いたメソッドなどの内部だけになる。
先のコードをローカル関数を使って書き直すと、IsMultipleOf3ローカル関数/IsMultipleOf5ローカル関数のスコープをFizzBuzz1メソッドの内部だけに限定できる(次のコード)。
public static string FizzBuzz2(int n)
{
if (IsMultipleOf3(n) && IsMultipleOf5(n))
return "Fizz Buzz";
if (IsMultipleOf3(n))
return "Fizz";
if (IsMultipleOf5(n))
return "Buzz";
return n.ToString();
// 以下のローカル関数のスコープは、このFizzBuzz2メソッドの中だけ
bool IsMultipleOf3(int m) => (m % 3) == 0;
bool IsMultipleOf5(int m) => (m % 5) == 0;
}
ローカル関数IsMultipleOf3/IsMultipleOf5のスコープは、それが置かれているFizzBuzz2メソッドの中だけである。FizzBuzz2メソッド専用のメソッドというわけだ。
ここではローカル関数をラムダ式で記述したが、もちろん中括弧で囲む通常のメソッドの書き方でも構わない。
ローカル関数の書き方
ローカル関数の書き方は通常のメソッドとほとんど同じだが、次のような違いがある。
- アクセス修飾子は書けない: publicやprivateを指定できない。スコープは、それが置かれたメンバー内と決まっているからである
- staticキーワードは書けない: それが置かれたメンバーと同じになると考えればよい。上のコード例ではstaticキーワードは付いていないが、staticメソッドの中から呼び出している
- ローカル関数自身やその引数に属性は付けられない
ローカル関数は、それを置くメソッドなどの中のどこに書いてもよい。先頭に書いてもよいし、途中に書いても、また、上のコード例のように末尾に書いてもよい。ただし、可読性の観点からは、先頭か末尾にまとめて置く方がよいだろう(筆者の好みは末尾だ。メソッドのシグネチャの直下にメソッドのあらましが書かれており、詳細はその下を見るという形になる)。
ローカル関数の中では、それが置かれたメンバー内でアクセスできる変数や引数などにアクセスできる(次のコード)。逆はアクセスできない。
public static string FizzBuzz3(int n)
{
if (IsMultipleOf3() && IsMultipleOf5())
return "Fizz Buzz";
if (IsMultipleOf3())
return "Fizz";
if (IsMultipleOf5())
return "Buzz";
return n.ToString();
// ローカル関数内でも、このFizzBuzz3メソッドの引数や変数を使える
bool IsMultipleOf3() => (n % 3) == 0;
bool IsMultipleOf5() => (n % 5) == 0;
}
FizzBuzz3メソッドに渡された引数nを、ローカル関数の中で使っている。この例にはないが、FizzBuzz3メソッド内で宣言したローカル変数も、ローカル関数の中で使える。
Copyright© Digital Advantage Corp. All Rights Reserved.