デリゲートとイベントはC#で提案されている独自の概念です。簡単に言えば、デリゲートはC#での「関数ポインター」のより良い実装であり、イベントはC#での「コールバック関数」のより良い実装です。
委任の種類と割り当て
デリゲートは関数ポインタに似ており、参照型であり、関数を参照していると見なすことができます。デリゲートが参照する関数には特定のタイプがあります。デリゲートタイプは関数のシグネチャ(パラメータタイプと関数の順序)を表すため、タイプセーフと見なすことができます。つまり、デリゲートタイプはどのタイプも参照できません。これは互換性がありません。デリゲートインスタンスは、特定の関数、および特定のクラスのインスタンスメソッドまたは静的メソッドを表すことができるため、デリゲートは関数のパッケージまたは参照として理解できます。
1.デリゲートタイプとデリゲート変数の宣言
宣言の形式は次のとおりです。
修饰符 delegate 返回类型 委托名 (参数列表);
仮パラメータリストはデリゲートの署名を指定し、結果タイプはデリゲートの戻り値の型を指定します。
例:
public delegate double MyDelegate(double x);
これにより、型と呼ばれるデリゲート型が宣言され、MyDelegate
参照できる関数は次のようになります。double
パラメーターを受け取り、戻り値の型もdouble
です。
デリゲートは型であるため、通常class
は宣言と並列class
です。したがって、定義に記述しないでください。(class
内部に記述されている場合は、ネストされた型になります。)
デリゲート型の変数を宣言することは、通常の変数を宣言することと同じです。
委托类型名 委托变量名;
といった:
MyDelegate d;
2.代理人のインスタンス化
デリゲートをインスタンス化し、デリゲートのインスタンスを作成する方法は次のとおりです。
new 委托类型名(方法名);
メソッド名は、特定のクラスの静的メソッド名、またはオブジェクトインスタンスのインスタンスメソッド名にすることができます。例えば:
MyDelegate d1 = new MyDelegate(System.Math.Sqrt);
MyDelegate d2 = new MyDelegate(obj.MyMethod);
ここでのメソッドの署名と戻り値の型は、デリゲート型によって宣言されたものと一致している必要があります。つまり、デリゲートは型厳密で型安全です。
委任された割り当ての省略形(シンタックスシュガー): C#2.0以降では、次のように省略できます。
MyDelegate d1 = System.Math.Sqrt;
MyDelegate d2 = obj.MyMethod;
3.電話を委任する
デリゲートの重要な機能は、メソッドを呼び出すときに、デリゲートがメソッドが属するオブジェクトのタイプを気にする必要がないことです。提供されたメソッドの署名がデリゲートの署名と一致する必要があるだけです。
この例DelegateIntergral.cs
では、数学関数の積分を計算します。
delegate double Fun (double x);
class DelegateIntegral
{
static void Main (string[] args) {
Console.WriteLine ("Hello World!");
Fun fun = new Fun (Math.Sin);
double d = Integral (fun, 0, Math.PI / 2, 1e-4);
Console.WriteLine (d);
Fun fun1 = new Fun (Linear);
double d1 = Integral (fun1, 0, 2, 1e-3);
Console.WriteLine (d1);
Rnd rnd = new Rnd ();
double d2 = Integral (new Fun (rnd.Num), 0, 1, 1e-2);
Console.WriteLine (d2);
}
public static double Linear (double a) {
return a * 2 + 1;
}
public static double Integral (Fun f, double a, double b, double eps) {
// 积分计算
int n, k;
double fa, fb, h, t1, p, s, x, t = 0;
fa = f (a);
fb = f (b);
// 迭代初值
n = 1;
h = b - a;
t1 = h * (fa + fb) / 2.0;
p = double.MaxValue;
// 迭代计算
while (p >= eps) {
s = 0.0;
for (int i = 0; i < n; i++) {
x = a + (i + 0.5) * h;
s += f (x);
}
t = (t1 + h * s) / 2.0;
p = Math.Abs (t1 - t);
t1 = t;
n += n;
h /= 2.0;
}
return t;
}
}
class Rnd
{
Random r = new Random ();
public double Num (double x) {
return r.NextDouble ();
}
}
4.委託合併(マルチキャスト)
デリゲートは、関数ポインターの単なるラッパーではありません。マージ可能なデリゲートを使用すると、他の言語の関数ポインターよりもはるかに優れた関数が得られます。
デリゲートの組み合わせ性はマルチキャストとも呼ばれ、簡単に言えば、複数の関数を一度に呼び出すことができます。マージされたデリゲートは、実際には複数の関数のパッケージ化であり、そのようなデリゲートへの呼び出しは、実際には、パッケージ化された各関数へのすべての呼び出しです。これらの機能の多くは、まとめてデリゲートのコールリストと呼ばれます。
実際、コンパイラはすべてのデリゲートをSystem.MulticastDelegate
サブクラスに変換し、デリゲート呼び出しはInvoke()
メソッド呼び出しに変換されます。
委託された加算および減算演算の結果null
。関数が含まれていない場合、結果はです。に等しいデリゲートを呼び出すと、null
実行時にNullReferenceException
例外が発生するため、デリゲートを呼び出す前に、それがであるかどうかを判断する必要がありnull
ます。マルチキャスト委任の使用
例DelegateMulticast.cs
:
delegate void D(int x);
class DelegateMulticast
{
static void Main(string[] args) {
Console.WriteLine("Hello World!");
DelegateMulticast.Test();
}
public static void M1(int i) {
Console.WriteLine("DelegateMulticast.M1: " + i);
}
public static void M2(int i) {
Console.WriteLine("DelegateMulticast.M2: " + i);
}
public void M3(int i) {
Console.WriteLine("DelegateMulticast.M3: " + i);
}
public static void Test() {
D cd1 = new D(DelegateMulticast.M1);
cd1(-1); // call M1
D cd2 = new D(DelegateMulticast.M2);
cd2(-2); // call M2
D cd3 = cd1 + cd2;
cd3(10); // call M1 then M2
cd3 += cd1;
cd3(20); // call M1, M2, then M1
DelegateMulticast d = new DelegateMulticast();
D cd4 = new D(d.M3);
cd3 += cd4;
cd3(30); // call M1, M2, M1, then M3
cd3 -= cd1; // remove last M1
cd3(40); // call M1, M2, then M3
cd3 -= cd4;
cd3(50); // call M1 then M2
cd3 -= cd2;
cd3(60); // call M1
cd3 -= cd2; // impossible removal is benign
cd3(60); // call M1
Console.WriteLine(cd3 == null); // False
cd3 -= cd1; // invocation list is empty
Console.WriteLine(cd3 == null); // True
//cd3 (70); // System.NullReferenceException thrown
cd3 -= cd1; // impossible removal
Console.WriteLine(cd3 == null); // True
}
}
5.委託された転換と平等
すべてのデリゲート型は、暗黙的にSystem.MulticastDelegate
それから派生します。System.MulticastDelegate
クラスメンバーには、メンバーアクセスを使用してアクセスできます。ただしSystem.MulticastDelegate
、デリゲート型ではなく、クラス型です。デリゲート型は暗黙的に封印されています。つまり、デリゲート型から派生させることはできません。
C#のデリゲート型は名前と同等であり、構造的に同等ではありません。つまり、名前が異なる2つのデリゲート型は、同じ署名と同じ戻り値の型を持っていても、異なるデリゲート型と見なされます。といった:
delegate void D(int a);
delegate void E(int a);
その場合、DとEは2つの異なるタイプのデリゲートであり、相互に変換することはできません。
2つのデリゲートインスタンスの場合、等式演算子(==
)には特別な意味があります。これは、各デリゲート型が事前定義された比較演算子を暗黙的に提供するためです。次の状況では、2つのデリゲートインスタンスは等しいと見なされます。
- 両方とも、
null
または同じデリゲートインスタンスへの参照です。 - デリゲートにメソッドが1つしかない場合、それらは同じオブジェクトの同じ静的メソッドまたは同じインスタンスメソッドを指します。
- デリゲートに複数のメソッドしかない場合、メソッドの数は同じであり、対応するメソッドは同じであり、順序は同じです。
上記の定義によれば、異なるタイプのコミッションも同じである可能性があります。
例DelegateEqual.csは、2つのデリゲートが等しいかどうかを判別します。
delegate void D(int a);
delegate void E(int a);
class DelegateEqual
{
public static void M(int a) {
}
public static void Test() {
E e = new E(M);
D d = new D(M);
e += e;
d += d;
//e = (E)d; // 编译错误
//d += e;
Console.WriteLine(d.Equals(e));
//Console.WriteLine(d == e); // why??? 编译错误?
Console.WriteLine(d);
}
}