「C Sharpの基礎 - デリゲート」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
(文字列「source lang」を「syntaxhighlight lang」に置換)
20行目: 20行目:
デリゲート型を作成するには、以下のように<code>delegate</code>キーワードを使用する。<br>
デリゲート型を作成するには、以下のように<code>delegate</code>キーワードを使用する。<br>
これにより、Delegate型(正確には、MulticastDelegate型)のサブクラスが定義される。<br>
これにより、Delegate型(正確には、MulticastDelegate型)のサブクラスが定義される。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  // int型を引数にとり、int型を返すメソッドを参照するデリゲート型
  // int型を引数にとり、int型を返すメソッドを参照するデリゲート型
  public delegate int IntToInt(int value);
  public delegate int IntToInt(int value);
38行目: 38行目:
<br>
<br>
次に、クラスメソッドを参照するデリゲートのインスタンスを作成する。<br>
次に、クラスメソッドを参照するデリゲートのインスタンスを作成する。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  // インスタンスを作成するデリゲート型
  // インスタンスを作成するデリゲート型
  // int型を引数にとり、int型を返すメソッドを参照するデリゲート型
  // int型を引数にとり、int型を返すメソッドを参照するデリゲート型
44行目: 44行目:
  </source>
  </source>
<br>
<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  // 参照するクラスとメソッド
  // 参照するクラスとメソッド
  public class Calculator
  public class Calculator
55行目: 55行目:
  </source>
  </source>
<br>
<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  // デリゲートのインスタンスを作成
  // デリゲートのインスタンスを作成
  // AddOneメソッドを参照するIntToInt型
  // AddOneメソッドを参照するIntToInt型
69行目: 69行目:
デリゲートは、対象のインスタンスへの参照も内部的に保持しており、<br>
デリゲートは、対象のインスタンスへの参照も内部的に保持しており、<br>
そのインスタンスにアクセスするためのプロパティとして、<code>Delegate</code>型は<code>Target</code>プロパティを持っている。<br>
そのインスタンスにアクセスするためのプロパティとして、<code>Delegate</code>型は<code>Target</code>プロパティを持っている。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  // インスタンスを作成するデリゲート型
  // インスタンスを作成するデリゲート型
  // int型を引数にとり、int型を返すメソッドを参照するデリゲート型
  // int型を引数にとり、int型を返すメソッドを参照するデリゲート型
75行目: 75行目:
  </source>
  </source>
<br>
<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  public class Multiplier
  public class Multiplier
  {
  {
93行目: 93行目:
<br>
<br>
MultiplierクラスのインスタンスのCalcメソッドを参照するデリゲートのインスタンスを作成するには、以下のように記述する。<br>
MultiplierクラスのインスタンスのCalcメソッドを参照するデリゲートのインスタンスを作成するには、以下のように記述する。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  Multiplier doubler = new Multiplier(2);    // Calcメソッドは、引数で渡した数の2倍の数を返す
  Multiplier doubler = new Multiplier(2);    // Calcメソッドは、引数で渡した数の2倍の数を返す
  IntToInt doublerIntToInt = doubler.Calc;  // doublerのCalcを参照するデリゲートを生成
  IntToInt doublerIntToInt = doubler.Calc;  // doublerのCalcを参照するデリゲートを生成
108行目: 108行目:
<br>
<br>
以下の例では、Func<T, TResult>型のデリゲートのインスタンスを生成している。<br>
以下の例では、Func<T, TResult>型のデリゲートのインスタンスを生成している。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  public static int AddOne(int value)
  public static int AddOne(int value)
  {
  {
165行目: 165行目:
7行目の<code>predicate(element)</code>では、T型のelementを引数に、Func<T, bool>型のデリゲートであるpredicateが参照しているメソッドを呼び出している。<br>
7行目の<code>predicate(element)</code>では、T型のelementを引数に、Func<T, bool>型のデリゲートであるpredicateが参照しているメソッドを呼び出している。<br>
(<code>predicate(element)</code>は、<code>predicate.Invoke(element)</code>とも記述できる)<br>
(<code>predicate(element)</code>は、<code>predicate.Invoke(element)</code>とも記述できる)<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  public static int CountList<T>(List<T> list, Func<T, bool> predicate)
  public static int CountList<T>(List<T> list, Func<T, bool> predicate)
  {
  {
186行目: 186行目:
匿名関数を使用しない場合、デリゲートを作成するためだけに使用するメソッドの定義は、<br>
匿名関数を使用しない場合、デリゲートを作成するためだけに使用するメソッドの定義は、<br>
デリゲートのインスタンスを生成する場所と離れた位置に記述する必要があるため、可読性が悪い。<br>
デリゲートのインスタンスを生成する場所と離れた位置に記述する必要があるため、可読性が悪い。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  public static bool IsLengthLessThan5(string str)
  public static bool IsLengthLessThan5(string str)
  {
  {
210行目: 210行目:
匿名関数とは、デリゲートのインスタンスを生成すると同時に、インラインでメソッドを定義するためのものである。<br>
匿名関数とは、デリゲートのインスタンスを生成すると同時に、インラインでメソッドを定義するためのものである。<br>
以下の例では、上記のIsLengthLessThan5メソッドを匿名関数に変更している。<br>
以下の例では、上記のIsLengthLessThan5メソッドを匿名関数に変更している。<br>
  <source lang="csharp">
  <syntaxhighlight lang="csharp">
  public static void Main (String[] args)
  public static void Main (String[] args)
  {
  {

2021年11月15日 (月) 01:00時点における版

概要

  • デリゲートとは、メソッドを参照する型である。
  • LINQ等で使用されるAction<T>Func<T, TResult>は、デリゲート型の1つである。
  • デリゲート型を作成するためだけに、クラスメソッドかインスタンスメソッドを定義するのは面倒である。
  • 匿名関数を使用すれば、メソッドを定義せずにインラインに処理を記述して、デリゲート型を作成することができる。
  • 匿名関数には2種類あり、その1つがラムダ式である。(正確には、C#のラムダ式はシンタックスであり、匿名関数ではない)
  • ラムダ式はデリゲート型の作成だけでなく、式ツリー型の生成にも使用できる。
  • 匿名関数は、コンパイラがクラスメソッド・インスタンスメソッド・クラス等を内部的に作成して、それを参照するデリゲートを作っている。



デリゲート - Delegate型

C#におけるデリゲートとは、メソッドを参照する型のことである。

メソッドを参照するデリゲートを、引数として渡したり、戻り値を返している。
このデリゲートの存在が、LINQやReactive Extensionsを支えている。

全てのデリゲートは、Delegate型から派生した型である。

デリゲート型を作成するには、以下のようにdelegateキーワードを使用する。
これにより、Delegate型(正確には、MulticastDelegate型)のサブクラスが定義される。

<syntaxhighlight lang="csharp">
// int型を引数にとり、int型を返すメソッドを参照するデリゲート型
public delegate int IntToInt(int value);

// string型を引数にとり、int型を返すメソッドを参照するデリゲート型
public delegate int StringToInt(string value);

// 引数が無く、int型を返すメソッドを参照するデリゲート型
public delegate int ReturnInt();

// int型を引数にとり、戻り値が無いメソッドを参照するデリゲート型
public delegate void ActionInt(int value);

// 2つのint型を引数にとり、int型を返すメソッドを参照するデリゲート型
public delegate int IntIntToInt(int value0, int value1);
</source>


次に、クラスメソッドを参照するデリゲートのインスタンスを作成する。

<syntaxhighlight lang="csharp">
// インスタンスを作成するデリゲート型
// int型を引数にとり、int型を返すメソッドを参照するデリゲート型
public delegate int IntToInt(int value);
</source>


<syntaxhighlight lang="csharp">
// 参照するクラスとメソッド
public class Calculator
{
   public static int AddOne (int value)
   {
      return value + 1;
   }
}
</source>


<syntaxhighlight lang="csharp">
// デリゲートのインスタンスを作成
// AddOneメソッドを参照するIntToInt型
IntToInt addOne = Calculator.AddOne;

// Calculatorクラス内ならば、以下の記述でもよい
// IntToInt addOne = AddOne;
</source>


最後に、インスタンスメソッドを参照するデリゲートのインスタンスを作成する。

デリゲートがインスタンスメソッドを参照する場合、どのインスタンスのメソッドを参照しているかの情報が必要である。
デリゲートは、対象のインスタンスへの参照も内部的に保持しており、
そのインスタンスにアクセスするためのプロパティとして、Delegate型はTargetプロパティを持っている。

<syntaxhighlight lang="csharp">
// インスタンスを作成するデリゲート型
// int型を引数にとり、int型を返すメソッドを参照するデリゲート型
public delegate int IntToInt(int value);
</source>


<syntaxhighlight lang="csharp">
public class Multiplier
{
   readonly int number;

   public Multiplier(int number)
   {
      this.number = number;
   }

   public int Calc(int v)
   {
      return number * v;
   }
}
</source>


MultiplierクラスのインスタンスのCalcメソッドを参照するデリゲートのインスタンスを作成するには、以下のように記述する。

<syntaxhighlight lang="csharp">
Multiplier doubler = new Multiplier(2);    // Calcメソッドは、引数で渡した数の2倍の数を返す
IntToInt doublerIntToInt = doubler.Calc;   // doublerのCalcを参照するデリゲートを生成

Multiplier trippler = new Multiplier(3);   // Calcメソッドは引数で渡した数の3倍の数を返す
IntToInt tripplerIntToInt = trippler.Calc; // tripplerのCalcを参照するデリゲートを生成
</source>



デリゲート - Action型とFunc型

Action<T>は、T型の引数をとり、戻り値は返さないメソッドを参照するジェネリックなデリゲート型である。
Func<T, TResult>は、T型の引数をとり、TResult型を返すメソッドを参照するジェネリックなデリゲート型である。
これらは、デリゲート型の1つである。

以下の例では、Func<T, TResult>型のデリゲートのインスタンスを生成している。

<syntaxhighlight lang="csharp">
public static int AddOne(int value)
{
   return value + 1;
}

public static int GetLength(string value)
{
   return value.Length;
}

public static void Main(string[] args)
{
   // intを引数に取りintを返すAddOneメソッドを参照するデリゲートを生成
   Func<int, int> addOneDelegate = AddOne;

   // stringを引数に取りintを返すGetLenghtメソッドを参照するデリゲートを生成
   Func<string, int> GetLengthDelegate = GetLength;

   // doubleを引数に取りdoubleを返すSystem.Math.Absメソッドを参照するデリゲートを生成
   Func<double, double> doubleFloatDelegate = Math.Abs;
}
</source>


Func<T, TResult>はジェネリックなデリゲート型なので、適切に型パラメータを設定すれば、様々なメソッドを参照できます。

Func<T, TResult>以外のジェネリクなデリゲート型には、更に多くの引数をとるAction型やFunc型のデリゲート型も存在する。
これらのジェネリックなデリゲート型は、Action<T>型を除き、C# 3.0 / .NET 3.5で追加された。

以下に、メソッドを参照することができるジェネリクなデリゲート型の例を示す。

  • Action型
    引数とらず、戻り値を返さないメソッドを参照するデリゲート型
  • Action<T>型
    T型の引数をとり、戻り値を返さないメソッドを参照するデリゲート型
  • Action<T1, T2>型
    T1型、T2型の引数をとり、戻り値を返さないメソッドを参照するデリゲート型
  • Func<TResult>型
    引数とらず、TResult型の戻り値を返すメソッドを参照するデリゲート型
  • Func<T1, T2, TResult>型
    T1型、T2型の引数をとり、TResult型の戻り値を返すメソッドを参照するデリゲート型


また、Action型やFunc型以外のデリゲート型も存在する。
LINQ等ではAction型やFunc型が多く使用されるが、それら以外のデリゲート型もクラスライブラリに存在する。

Predicate<T>型というデリゲート型もその1つである。
このデリゲート型は、ListクラスのFindAllメソッドの引数として使用されており、ListクラスのConvertAllメソッド等でも使用されている。
他のデリゲート型には、Convertor<TInput, TOutput>型、ListクラスのSortメソッドで使用されているComparison<T>型等がある。


匿名関数

上記に記載した通り、デリゲートはメソッドを参照する型であり、LINQ等で使用するFunc<T, TResult>型等はデリゲートの型の1つである。

匿名関数を説明するため、以下の例では、List<T>型とFunc<T, TResult>を引数とするCountListメソッドを記述している。
第1引数であるList<T>型の変数の要素のうち、第2引数Func<T, bool>型のデリゲートを適用して、真になる要素の数を数えるメソッドである。

7行目のpredicate(element)では、T型のelementを引数に、Func<T, bool>型のデリゲートであるpredicateが参照しているメソッドを呼び出している。
(predicate(element)は、predicate.Invoke(element)とも記述できる)

<syntaxhighlight lang="csharp">
public static int CountList<T>(List<T> list, Func<T, bool> predicate)
{
   int count = 0;
   foreach (T element in list)
   {
      // predicateが参照するメソッドを、elementを引数に渡して呼び出す
      if(predicate(element))
      { 
         count++;  // 真ならカウンタをインクリメント
      }
   }

   return count;
}
</source>


以下の例では、上記のCountListメソッドを使用して、List<string>型namesの要素のうち、文字列の長さが5未満のものを数えている。

匿名関数を使用しない場合、デリゲートを作成するためだけに使用するメソッドの定義は、
デリゲートのインスタンスを生成する場所と離れた位置に記述する必要があるため、可読性が悪い。

<syntaxhighlight lang="csharp">
public static bool IsLengthLessThan5(string str)
{
   return str.Length < 5;
}

public static void Main(String[] args)
{
   var names = new List<string>
   {
      "Taro",
      "Jiro",
      "Saburo"
   };

   // IsLengthLessThan5を参照するFunc<string, bool>型のデリゲートを生成
   Func<string, bool> predicate = IsLengthLessThan5;

   int count = CountList(names, predicate);
}
</source>


匿名関数とは、デリゲートのインスタンスを生成すると同時に、インラインでメソッドを定義するためのものである。
以下の例では、上記のIsLengthLessThan5メソッドを匿名関数に変更している。

<syntaxhighlight lang="csharp">
public static void Main (String[] args)
{
   List<string> names = new List<string>
   {
       "Taro",
       "Jiro",
       "Saburo"
   };

   // 匿名関数を参照するFunc<string, bool>型のデリゲートを作成
   // 匿名関数は、デリゲートの生成とメソッドの定義が近い位置にある
   Func<string, bool> predicate = delegate (string str)
   {
      return str.Length < 5;
   };

   int count = CountList(names, predicate);
}
</source>


上記の例は、匿名関数の一例である。
匿名関数を使用することにより、インラインで処理を記述してデリゲートを生成することができ、可読性が上がる。

しかし、C# 3.0以降では匿名関数を使用せずに、ラムダ式を使用すべきである。
ラムダ式については、こちらのページを参照すること。