サーチ…
備考
概要
デリゲート型は、特定のメソッドシグネチャを表す型です。この型のインスタンスは、一致する署名を持つ特定のメソッドを参照します。メソッドのパラメータにはデリゲート型があるため、この1つのメソッドに別のメソッドへの参照を渡すことができます。
組み込みデリゲート型: Action<...>
、 Predicate<T>
およびFunc<...,TResult>
System
名前空間が含まれているAction<...>
Predicate<T>
とFunc<...,TResult>
「...」0および16ジェネリック型パラメータの間を表すデリゲートを、(0パラメータのため、 Action
非ですジェネリック)。
Func
は戻り値の型がTResult
に一致するメソッドを表し、 Action
は戻り値のないメソッド(void)を表します。どちらの場合も、追加のジェネリック型パラメータは、メソッドパラメータと順番に一致します。
Predicate
はブール型の戻り型を持つメソッドを表し、Tは入力パラメータです。
カスタムデリゲートの種類
名前付きのデリゲート型は、 delegate
キーワードを使用して宣言できます。
デリゲートの呼び出し
デリゲートは、メソッドと同じ構文を使用して呼び出すことができます。デリゲートインスタンスの名前と、パラメータを含む括弧が続きます。
代理人に割り当てる
代理人は、次の方法で割り当てることができます。
- 名前付きメソッドの割り当て
- ラムダを使用した匿名メソッドの割り当て
-
delegate
キーワードを使用した名前付きメソッドの割り当て。
デリゲートの結合
+
演算子を使用すると、複数のデリゲートオブジェクトを1つのデリゲートインスタンスに割り当てることができます。 -
演算子を使用すると、別のデリゲートからコンポーネントデリゲートを削除できます。
名前付きメソッドの代理人の基礎となる参照
デリゲートに名前付きメソッドを割り当てるとき、次の場合に同じ基本オブジェクトを参照します。
それらはクラスの同じインスタンス上の同じインスタンスメソッドです
それらはクラス上で同じ静的メソッドです
public class Greeter { public void WriteInstance() { Console.WriteLine("Instance"); } public static void WriteStatic() { Console.WriteLine("Static"); } } // ... Greeter greeter1 = new Greeter(); Greeter greeter2 = new Greeter(); Action instance1 = greeter1.WriteInstance; Action instance2 = greeter2.WriteInstance; Action instance1Again = greeter1.WriteInstance; Console.WriteLine(instance1.Equals(instance2)); // False Console.WriteLine(instance1.Equals(instance1Again)); // True Action @static = Greeter.WriteStatic; Action staticAgain = Greeter.WriteStatic; Console.WriteLine(@static.Equals(staticAgain)); // True
デリゲート型の宣言
次の構文は、作成delegate
名を持つタイプNumberInOutDelegate
取る方法表す、 int
と返すint
。
public delegate int NumberInOutDelegate(int input);
これは次のように使用できます。
public static class Program
{
static void Main()
{
NumberInOutDelegate square = MathDelegates.Square;
int answer1 = square(4);
Console.WriteLine(answer1); // Will output 16
NumberInOutDelegate cube = MathDelegates.Cube;
int answer2 = cube(4);
Console.WriteLine(answer2); // Will output 64
}
}
public static class MathDelegates
{
static int Square (int x)
{
return x*x;
}
static int Cube (int x)
{
return x*x*x;
}
}
example
デリゲートインスタンスは、同じ方法で実行されるSquare
方法。デリゲートインスタンスは、文字通り、呼び出し元の代理人として機能します。呼び出し元がデリゲートを呼び出し、デリゲートがターゲットメソッドを呼び出します。このインダイレクションは、呼び出し元をターゲットメソッドから切り離します。
あなたはジェネリックデリゲート型を宣言することができ、その場合には、あなたはタイプが共変(であることを指定してもよいout
()または反変in
型引数の一部では)。例えば:
public delegate TTo Converter<in TFrom, out TTo>(TFrom input);
他のジェネリック型と同様、ジェネリックデリゲート型にはwhere TFrom : struct, IConvertible where TTo : new()
などの制約があります。
イベントハンドラ型など、マルチキャスト代理人に使用されるデリゲート型の共変および反差別を避けます。これは、分散のために実行時の型がコンパイル時の型と異なる場合、連結( +
)が失敗する可能性があるためです。たとえば、以下を回避します。
public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);
代わりに、不変のジェネリック型を使用してください:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
また、いくつかのパラメータがref
またはout
によって変更されるデリゲートもサポートされています。
public delegate bool TryParser<T>(string input, out T result);
(サンプル使用TryParser<decimal> example = decimal.TryParse;
)、または最後のパラメータにparams
修飾子がある場合は委譲します。デリゲートタイプにはオプションのパラメータを指定できます(デフォルト値を指定します)。デリゲート型では、シグネチャや戻り値の型にint*
やchar*
などのポインタ型を使用できます( unsafe
キーワードを使用する)。デリゲート型とそのパラメータは、カスタム属性を持つことができます。
ザ・ファンク 、アクション述語デリゲート型
System名前空間が含まれているFunc<..., TResult>
型を返す、0〜15の一般的なパラメータでデリゲート型をTResult
。
private void UseFunc(Func<string> func)
{
string output = func(); // Func with a single generic type parameter returns that type
Console.WriteLine(output);
}
private void UseFunc(Func<int, int, string> func)
{
string output = func(4, 2); // Func with multiple generic type parameters takes all but the first as parameters of that type
Console.WriteLine(output);
}
Systemネームスペースには、ジェネリックパラメータの数が異なる( Action<...>
デリゲート型も含まれています。これはFunc<T1, .., Tn>
と似ていますが、常にvoid
返しvoid
。
private void UseAction(Action action)
{
action(); // The non-generic Action has no parameters
}
private void UseAction(Action<int, string> action)
{
action(4, "two"); // The generic action is invoked with parameters matching its type arguments
}
Predicate<T>
はFunc
一種ですが、常にbool
返します。述語とは、カスタム条件を指定する方法です。入力値と述部内で定義されたロジックの値に応じて、 true
またはfalse
が返されます。したがって、 Predicate<T>
はFunc<T, bool>
と同じように動作し、両方とも初期化して同じ方法で使用できます。
Predicate<string> predicate = s => s.StartsWith("a");
Func<string, bool> func = s => s.StartsWith("a");
// Both of these return true
var predicateReturnsTrue = predicate("abc");
var funcReturnsTrue = func("abc");
// Both of these return false
var predicateReturnsFalse = predicate("xyz");
var funcReturnsFalse = func("xyz");
Predicate<T>
かFunc<T, bool>
のどちらを使用するかは、実際には意見の問題です。 Predicate<T>
はおそらく著者の意図をより表現していますが、 Func<T, bool>
はC#開発者の大部分に馴染みそうです。
それに加えて、特に別のAPIとやり取りするときに、オプションのうちの1つだけが利用可能な場合もあります。例えば、 List<T>
とArray<T>
一般的にそのメソッドに対してPredicate<T>
を取ります。ほとんどのLINQ拡張ではFunc<T, bool>
しか受け入れません。
デリゲートに名前付きメソッドを割り当てる
名前付きのメソッドは、シグネチャが一致するデリゲートに割り当てることができます。
public static class Example
{
public static int AddOne(int input)
{
return input + 1;
}
}
Func<int,int> addOne = Example.AddOne
Example.AddOne
取りint
と返すint
、その署名は、デリゲートと一致するFunc<int,int>
。 Example.AddOne
は、一致するシグネチャを持つためaddOne
直接割り当てることができます。
代議員の平等
.Equals()
を呼び出すと、参照の等価性が比較されます。
Action action1 = () => Console.WriteLine("Hello delegates");
Action action2 = () => Console.WriteLine("Hello delegates");
Action action1Again = action1;
Console.WriteLine(action1.Equals(action1)) // True
Console.WriteLine(action1.Equals(action2)) // False
Console.WriteLine(action1Again.Equals(action1)) // True
これらの規則は、マルチキャストデリゲートで+=
または-=
を実行する場合にも適用されます(たとえば、イベントのサブスクライブおよびサブスクライブ解除の場合)。
ラムダによる代理人への割り当て
Lambdasを使用して、デリゲートに割り当てる匿名メソッドを作成できます。
Func<int,int> addOne = x => x+1;
このように変数を作成するときは、型の明示的宣言が必要であることに注意してください。
var addOne = x => x+1; // Does not work
代理人をパラメータとして渡す
デリゲートは、型付きの関数ポインタとして使用できます。
class FuncAsParameters
{
public void Run()
{
DoSomething(ErrorHandler1);
DoSomething(ErrorHandler2);
}
public bool ErrorHandler1(string message)
{
Console.WriteLine(message);
var shouldWeContinue = ...
return shouldWeContinue;
}
public bool ErrorHandler2(string message)
{
// ...Write message to file...
var shouldWeContinue = ...
return shouldWeContinue;
}
public void DoSomething(Func<string, bool> errorHandler)
{
// In here, we don't care what handler we got passed!
...
if (...error...)
{
if (!errorHandler("Some error occurred!"))
{
// The handler decided we can't continue
return;
}
}
}
}
デリゲートの結合(マルチキャストデリゲート)
加算+
および減算-
演算を使用してデリゲートインスタンスを結合できます。デリゲートには、割り当てられたデリゲートのリストが含まれています。
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace DelegatesExample {
class MainClass {
private delegate void MyDelegate(int a);
private static void PrintInt(int a) {
Console.WriteLine(a);
}
private static void PrintType<T>(T a) {
Console.WriteLine(a.GetType());
}
public static void Main (string[] args)
{
MyDelegate d1 = PrintInt;
MyDelegate d2 = PrintType;
// Output:
// 1
d1(1);
// Output:
// System.Int32
d2(1);
MyDelegate d3 = d1 + d2;
// Output:
// 1
// System.Int32
d3(1);
MyDelegate d4 = d3 - d2;
// Output:
// 1
d4(1);
// Output:
// True
Console.WriteLine(d1 == d4);
}
}
}
この例では、 d3
はd1
とd2
デリゲートの組み合わせであるため、呼び出されるとプログラムは1
とSystem.Int32
両方の文字列を出力します。
デリゲートを非void戻り値の型と組み合わせる:
マルチキャストデリゲートがnonvoid
返りの戻り値型を持つ場合、呼び出し元は最後に呼び出されるメソッドからの戻り値を受け取ります。上記のメソッドは引き続き呼び出されますが、戻り値は破棄されます。
class Program
{
public delegate int Transformer(int x);
static void Main(string[] args)
{
Transformer t = Square;
t += Cube;
Console.WriteLine(t(2)); // O/P 8
}
static int Square(int x) { return x * x; }
static int Cube(int x) { return x*x*x; }
}
t(2)
は最初にSquare
を呼び出し、次にCube
を呼び出します。 Squareの戻り値は破棄され、最後のメソッドの戻り値、つまりCube
は保持されます。
安全な呼び出しマルチキャストデリゲート
多分、マルチキャストデリゲートを呼びたいと思っていましたが、チェイン内のいずれかで例外が発生したとしても、呼び出しリスト全体を呼び出す必要があります。あなたは運が良かったので、リスト全体の実行が完了した後にAggregateException
スローする拡張メソッドを作成しました。
public static class DelegateExtensions
{
public static void SafeInvoke(this Delegate del,params object[] args)
{
var exceptions = new List<Exception>();
foreach (var handler in del.GetInvocationList())
{
try
{
handler.Method.Invoke(handler.Target, args);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if(exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
}
public class Test
{
public delegate void SampleDelegate();
public void Run()
{
SampleDelegate delegateInstance = this.Target2;
delegateInstance += this.Target1;
try
{
delegateInstance.SafeInvoke();
}
catch(AggregateException ex)
{
// Do any exception handling here
}
}
private void Target1()
{
Console.WriteLine("Target 1 executed");
}
private void Target2()
{
Console.WriteLine("Target 2 executed");
throw new Exception();
}
}
これは、
Target 2 executed
Target 1 executed
SaveInvoke
なしで直接呼び出すと、ターゲット2だけが実行されます。
デリゲート内のクロージャ
クロージャは、 Parent
メソッドの変数や親のスコープで定義されている他の匿名メソッドを使用できるインラインの匿名メソッドです。
基本的に、クロージャは後で実行できるコードブロックですが、最初に作成された環境を維持します。つまり、それを作成したメソッドのローカル変数などを使用することができます。メソッドの実行が終了しました。 - Jon Skeet
delegate int testDel();
static void Main(string[] args)
{
int foo = 4;
testDel myClosure = delegate()
{
return foo;
};
int bar = myClosure();
}
.NETのClosureから取られた例
関数の変換をカプセル化する
public class MyObject{
public DateTime? TestDate { get; set; }
public Func<MyObject, bool> DateIsValid = myObject => myObject.TestDate.HasValue && myObject.TestDate > DateTime.Now;
public void DoSomething(){
//We can do this:
if(this.TestDate.HasValue && this.TestDate > DateTime.Now){
CallAnotherMethod();
}
//or this:
if(DateIsValid(this)){
CallAnotherMethod();
}
}
}
クリーンなコーディングの精神で、上記のようなチェックと変換をFuncとしてカプセル化すると、コードを読みやすく理解しやすくなります。上記の例は非常に単純ですが、独自の異なる検証規則を持つ複数のDateTimeプロパティがあり、異なる組み合わせを確認したい場合はどうなりますか?それぞれがリターンロジックを確立したシンプルな1行Funcは、コードの見かけ上の複雑さを解消し、読みやすくすることができます。以下のFuncの呼び出しを考えて、どのように多くのコードがメソッドを乱雑にしているのか想像してみてください。
public void CheckForIntegrity(){
if(ShipDateIsValid(this) && TestResultsHaveBeenIssued(this) && !TestResultsFail(this)){
SendPassingTestNotification();
}
}