次の方法で共有


デリゲートとラムダ

デリゲートでは、特定のパラメーター リストと戻り値の型を持つメソッドへの参照を表す型を定義します。 パラメーター リストと戻り値の型が一致するメソッド (静的またはインスタンス) は、その型の変数に代入し、(適切な引数を使用して) 直接呼び出したり、別のメソッドに引数そのものとして渡してから呼び出すことができます。 次の例は、デリゲートの使い方を示しています。

using System;
using System.Linq;

public class Program
{
    public delegate string Reverse(string s);

    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Reverse rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}
  • public delegate string Reverse(string s); の行で、文字列パラメーターを受け取って文字列パラメーターを返すメソッドのデリゲート型が作成されます。
  • static string ReverseString(string s) メソッドは、定義済みのデリゲート型とまったく同じパラメーター リストと戻り値の型を持っており、デリゲートが実装されます。
  • Reverse rev = ReverseString; 行では、対応するデリゲート型の変数にメソッドを割り当てることができることを示しています。
  • Console.WriteLine(rev("a string")); 行では、デリゲート型の変数を使用してデリゲートを呼び出す方法を示しています。

開発プロセスを効率化するため、.NET にはプログラマが再利用できるデリゲート型のセットが含まれているため、新しい型を作成する必要はありません。 これらの型は Func<>Action<> および Predicate<> であり、新しいデリゲート型を定義しなくても使用できます。 使用を意図した方法で実行する必要がある 3 つの型には、いくつかの違いがあります。

  • Action<> は、デリゲートの引数を使用してアクションを実行する必要がある場合に使用されます。 カプセル化されるメソッドは、値を返しません。
  • Func<> は、通常、変換が手元にあるときに使用されます。つまり、デリゲートの引数を異なる結果に変換する必要があります。 予測が良い例です。 カプセル化されるメソッドは、指定された値を返します。
  • Predicate<> は、引数がデリゲートの条件を満たすかどうかを判断する必要がある場合に使用されます。 また、Func<T, bool> として書き込むこともできます。これは、メソッドがブール値を返すことを意味します。

ここで、上記の例を使用して、カスタム型の代わりに Func<> デリゲートを使用して書き換えることができます。 プログラムは引き続きまったく同じに実行されます。

using System;
using System.Linq;

public class Program
{
    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Func<string, string> rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}

このシンプルな例では、Main メソッドの外部にメソッドを定義することは、少し余分なようです。 .NET Framework 2.0 では、匿名デリゲートの概念が導入されました。これにより、追加の型やメソッドを指定しなくても "インライン" デリゲートを作成できます。

次の例では、匿名デリゲートによってリストが偶数だけにフィルター処理され、コンソールに出力されます。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(
          delegate (int no)
          {
              return (no % 2 == 0);
          }
        );

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

ご覧のように、デリゲートの本体は、他のデリゲートと同じく、単なる式のセットです。 しかし、それを別の定義にする代わりに、List<T>.FindAll メソッドへの呼び出しでそれをアド ホックで導入しました。

ただし、この方法でも、破棄できる多くのコードがまだ残ります。 このような場合にラムダ式が機能します。 ラムダ式 (または略して単に "ラムダ") は、C# 3.0 で統合言語クエリ (LINQ) のコア ビルディング ブロックの 1 つとして導入されました。 これらは、デリゲートの使用の利便性を高める構文です。 これらは、パラメーター リストとメソッド本体を宣言しますが、デリゲートに割り当てられない限り、独自の正式な ID を持ちません。 デリゲートの場合とは異なり、これらをイベント登録の右側として、またはさまざまな LINQ 句およびメソッド内に、直接割り当てることができます。

ラムダ式はデリゲートを指定するもう 1 つの方法であるため、上記のサンプルを匿名デリゲートの代わりにラムダ式を使用するように書き換えることができるようになる必要があります。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(i => i % 2 == 0);

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

上の例で、使用されているラムダ式は i => i % 2 == 0 です。 これも、デリゲートの使用の利便性を高める構文にすぎません。 内部で行われる処理は、匿名デリゲートの場合と似ています。

ここでも、ラムダは単なるデリゲートです。つまり、次のコード スニペットに示すように、ラムダは問題なくイベント ハンドラーとして使用することができます。

public MainWindow()
{
    InitializeComponent();

    Loaded += (o, e) =>
    {
        this.Title = "Loaded";
    };
}

このコンテキストでの += 演算子は、イベントをサブスクライブするために使用されます。 詳細については、「イベントのサブスクリプションとサブスクリプション解除を行う方法」を参照してください。

参考資料とリソース