特集
» 2017年07月19日 05時00分 公開

特集:C# 7の新機能詳説:第1回 明瞭なコーディングのために (2/3)

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]

ローカル関数

 ローカル関数とは、メソッドなどの中に書くメソッドだ。メソッドにアクセスできるスコープを限定できる。

メソッドのスコープを限定する

 メソッドのスコープは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;

privateなメソッドのスコープは、そのクラス全体
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;
}

ローカル関数のスコープは、それが置かれたメンバー内のみ(C# 7)
ローカル関数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;
}

ローカル関数の中で、それが置かれたメソッドの引数を使う例(C# 7)
FizzBuzz3メソッドに渡された引数nを、ローカル関数の中で使っている。この例にはないが、FizzBuzz3メソッド内で宣言したローカル変数も、ローカル関数の中で使える。

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。