ローカル関数とは、メソッドなどの中に書くメソッドだ。メソッドにアクセスできるスコープを限定できる。
メソッドのスコープは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;
このような状況は、例えばコードを明瞭にするために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;
}
ここではローカル関数をラムダ式で記述したが、もちろん中括弧で囲む通常のメソッドの書き方でも構わない。
ローカル関数の書き方は通常のメソッドとほとんど同じだが、次のような違いがある。
ローカル関数は、それを置くメソッドなどの中のどこに書いてもよい。先頭に書いてもよいし、途中に書いても、また、上のコード例のように末尾に書いてもよい。ただし、可読性の観点からは、先頭か末尾にまとめて置く方がよいだろう(筆者の好みは末尾だ。メソッドのシグネチャの直下にメソッドのあらましが書かれており、詳細はその下を見るという形になる)。
ローカル関数の中では、それが置かれたメンバー内でアクセスできる変数や引数などにアクセスできる(次のコード)。逆はアクセスできない。
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;
}
Copyright© Digital Advantage Corp. All Rights Reserved.