委托的使用 - 浅谈
一,声明委托
对于委托,定义它就是要告诉编译器,这种类型的委托表示哪种类型的方法.然后,必须创建该委托的一个或多个委托实例,编译器将在后台创建表示该委托的一个类.
因为定义委托基本上是定义一个新类,所以可以在定义类的任何相同地方定义委托.
在术语方面,和"类,对象"不同."类"表示的是较为广义的定义,"对象"表示类的实例.但是委托只有一个术语,在创建委托实例时,所创建的委托的实例仍然称为委托.
如下显示委托的声明方式:
1
2
|
//声明委托. private delegate string GetAString(); |
二,使用委托
如下例子:
1 int x = 5; 2 //通过委托的方式. 3 GetAString stringMethod = new GetAString(x.ToString); 4 Console.WriteLine(stringMethod());
也可以使用委托推断,只需将地址传递给委托类型的变量(在后台,C#编译器会做同样的处理,即会在后台创建一个委托实例 "newGetAString(x.ToString)"):
1 //使用委托推断,只需将地址传递给委托实例. 2 GetAString secondMethod = x.ToString;
实际上,调用委托时,给委托实例提供圆括号与调用委托的Invoke()方法,是完全相同的:
1 Console.WriteLine(stringMethod()); //委托方式1(使用圆括号的方式). 2 Console.WriteLine(stringMethod.Invoke()); //委托方式2(使用调用Invoke()方法的方式).
原因是对于委托变量stringMethod,C#编译器会调用stringMethod.Invoke()代替stringMethod().
值得注意的是,在给一个委托类型的变量赋值的时候,方法的名称不能带有"()"括号,上述例子,调用 x.ToString()方法,会返回一个不能赋予委托变量的字符串对象(而不是方法的地址).
委托的一个特征是,它们是类型安全的,可以确定被调用的方法的签名是正确的.但是委托不关心在什么类型上调用改方法,甚至不考虑方法是静态的,还是实例方法.
如下例子演示了委托可以使用实例方法,也可以使用静态方法:
Currency结构的声明:
1 namespace Wrox.ProCSharp.Delegates { 2 struct Currency { 3 public uint Dollars; 4 public ushort Cents; 5 6 public Currency(uint dollars, ushort cents) { 7 this.Dollars = dollars; 8 this.Cents = cents; 9 } 10 11 public override string ToString() { 12 return string.Format("${0}.{1,-2:00}", Dollars, Cents); 13 } 14 15 public static string GetCurrencyUnit() { 16 return "Dollar"; 17 } 18 19 public static explicit operator Currency(float value) { 20 checked { 21 uint dollars = (uint)value; 22 ushort cents = (ushort)((value - dollars) * 100); 23 return new Currency(dollars, cents); 24 } 25 } 26 27 public static implicit operator float(Currency value) { 28 return value.Dollars + (value.Cents / 100.0f); 29 } 30 31 public static implicit operator Currency(uint value) { 32 return new Currency(value, 0); 33 } 34 35 public static implicit operator uint(Currency value) { 36 return value.Dollars; 37 } 38 } 39 40 }
在住函数中调用:
1 using System; 2 3 namespace Wrox.ProCSharp.Delegates 4 { 5 class Program 6 { 7 private delegate string GetAString(); 8 9 static void Main() 10 { 11 int x = 40; 12 GetAString firstStringMethod = x.ToString; 13 Console.WriteLine("String is {0}", firstStringMethod()); 14 15 Currency balance = new Currency(34, 50); 16 17 // firstStringMethod references an instance method 18 firstStringMethod = balance.ToString; 19 Console.WriteLine("String is {0}", firstStringMethod()); 20 21 // firstStringMethod references a static method 22 firstStringMethod = new GetAString(Currency.GetCurrencyUnit); 23 Console.WriteLine("String is {0}", firstStringMethod()); 24 25 } 26 } 27 }
输出:
String is 40 String is $34.50 String is Dollar
再来看一个委托使用:
定义操作的方法的类:
1 namespace SimpleDelegates_Demo { 2 class MathOperations { 3 public static double MultiplyByTwo(double value) { 4 return value * 2; 5 } 6 7 public static double Square(double value) { 8 return value * value; 9 } 10 } 11 12 }
在主函数中使用:
1 using System; 2 3 namespace SimpleDelegates_Demo { 4 delegate double Operate(double input); 5 class Program { 6 static void Main(string[] args) { 7 Operate[] actions = { MathOperations.MultiplyByTwo, MathOperations.Square }; 8 //遍历每个委托实例. 9 foreach (Operate action in actions) { 10 ProcessAndDisplayResult(action, 2); 11 ProcessAndDisplayResult(action, 2.5); 12 ProcessAndDisplayResult(action, 5.2); 13 Console.WriteLine(); 14 } 15 } 16 17 static void ProcessAndDisplayResult(Operate action, double inputVal) { 18 Console.WriteLine("Input is [{0}],Result is [{1}]", inputVal, action(inputVal)); 19 } 20 } 21 }
output:
1 Input is [2],Result is [4] 2 Input is [2.5],Result is [5] 3 Input is [5.2],Result is [10.4] 4 5 Input is [2],Result is [4] 6 Input is [2.5],Result is [6.25] 7 Input is [5.2],Result is [27.04]
在这个例子中,我们将委托实例封装到一个数组中,然后遍历每个委托实例,然后传递遍历到特定的方法中调用,这说明使用委托的一种方式 - 即把方法组合到一个数组中来使用,这样就可以在循环中调用不同的方法了.
值得注意的是,这里 ProcessAndDisplayResult(Operate action, double inputVal) 不是多余的.
当我们在主函数的第10~12行中传递action委托实例到 ProcessAndDisplayResult(Operate action, double inputVal) 方法的时候,action就是委托表示的方法.
而在ProcessAndDisplayResult(Operate action, double inputVal)方法体中,也就是上面Program类中的第18行中的action(inputVal),或action(2)实际上调用这个方法,参数放在圆括号中.也就是说,action(inputVal),或action(2)实际上是调用action委托实例封装的方法.