首页 > Python基础教程 >
-
C#教程之委托(delegate)(2)
3.【IAsyncResult】.IsCompleted(是IAsyncResult对象的一个属性,该值指示异步操作是否已完成。不推荐)
1 IAsyncResult asyncResult = xxx.BeginInvoke(...); 2 while (!asyncResult.IsCompleted) 3 { 4 //正在等待中 5 }
内置委托(泛化委托)
.Net Framework 提供两个支持泛型的内置委托,分别是Action<>和Func<>,在System命名空间中定义,结合lambda表达式,可以提高开发效率。
使用方式如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //使用Action声明委托 6 Action<string> action = TestAction; 7 action.Invoke("action-demo-ok"); 8 //使用Func声明委托 9 Func<string, string> func = TestFunc; 10 string result = func.Invoke("func-demo-ok"); 11 Console.WriteLine(result); 12 Console.ReadKey(); 13 } 14 private static void TestAction(string o) 15 { 16 Console.WriteLine("TestAction方法执行成功:{0}", o); 17 } 18 private static string TestFunc(string o) 19 { 20 return "TestFunc方法执行成功:" + o; 21 } 22 /* 23 * 作者:Jonins 24 * 出处:http://www.cnblogs.com/jonins/ 25 */ 26 }
Action:无返回值的泛型委托,目前.NET Framework提供了17个Action委托,它们从无参数到最多16个参数。
public delegate void Action | |
Action | 无返回值的泛型委托 |
Action<int,string> | 传入参数int、string,无返回值的委托 |
Action<int,string,bool> | 传入参数int,string,bool,无返回值的委托 |
Action<bool,bool,bool,bool> | 传入4个bool型参数,无返回值的委托 |
Action最少0个参数,最多16个参数,无返回值。 |
Func:有返回值的泛型委托,.NET Framework提供了17个Func函数,允许回调方法返回值。
public delegate TResult Func | |
Func<int> | 无参,返回值为int的委托 |
Func<int,string> | 传入参数int,返回值为string类型的委托 |
Func<object,string,bool> | 传入参数为object, string 返回值为bool类型的委托 |
Func<T1,T2,,T3,int> 表示 | 传入参数为T1,T2,,T3(类型)返回值为int类型的委托 |
Func最少0个参数,最多16个参数,根据返回值泛型返回。必须有返回值,不可为void。 |
本质上Action和Func都为delegate ,在System命名空间中定义(in和out用来标识变量)
除此之外还有Predicate,它是固定返回值为bool类型的泛型委托。Action和Func足够使用这里不做介绍。
注意:
1.委托定义不要太多,微软仅在MSCorLib.dll中就有进50个委托类型,而且.NET Framework现在支持泛型,所以我们只需几个泛型委托(在System命名空间中定义)就能表示需要获取多达16个参数的方法。
2.如需获取16个以上参数,就必须定义自己的委托类型。所以建议尽量使用内置委托,而不是在代码中定义更多的委托类型,这样可以减少代码中的类型数量,同时简化编码。
3.如需使用ref或out关键字以传引用的方式传递参数,就需要定义自己的委托。
内置委托(泛化委托)参数协变&逆变
协变(out):假定S是B的子类,如果X(S)允许引用转换成X(B),那么称X为协变类。(支持“子类”向“父类”转换)
逆变(in):假定S是B的子类,如果X(B)允许引用转换成X(X),那么称X为协变类。(支持“父类”向“子类”转换)
正如泛化接口,泛型委托同样支持协变与逆变
1 public delegate void Action<in T>(T obj); 2 3 public delegate TResult Func<out TResult>();
Action在System命名空间中定义支持逆变(in)
1 Action<object> x =...; 2 3 Action<string> y = x;
Func在System命名空间中定义支持协变(out)
1 Func<string> x =...; 2 3 Func<object> y = x;
如果要定义一个泛化委托类型,最好按照如下准则:
1.将只用在返回值的类型参数标注为协变(out)
2.将只用在参数的类型参数标注为逆变(in)
委托的兼容性
了解委托的兼容性,更易于在使用委托时使我们构建的代码具有多态性。
1.类型的兼容性:即使签名相似,委托类也互不兼容。
1 delegate void D1(); 2 delegate void D2(); 3 ... 4 D1 d1=Method1; 5 D2 d2=d1;//编译时错误 6 D2 d2=new D2(d1);//这是允许的
如果委托实例执行相同的目标方法,则认为它们是等价的。
1 delegate void D(); 2 ... 3 D1 d1=Method1; 4 D2 d2=Method1; 5 Console.WriteLine(d1==d2);//True
如果多播委托按照相同的顺序应用相同的方法责任委托它们是等价的。
2.参数的兼容性:当调用一个方法时,可以给方法的参数提供大于其指定类型的变量。这是正常的多态行为。同样,委托也可以又大于其目标方法参数类型的参数,即逆变。
1 class Program 2 { 3 //委托接受string类型参数 4 delegate void NoReturnWithParameters(string o); 5 static void Main(string[] args) 6 { 7 NoReturnWithParameters noReturnWithParameters = new NoReturnWithParameters(Test); 8 noReturnWithParameters("demo-ok"); 9 Console.ReadKey(); 10 } 11 //目标方法接受object类型参数 12 static void Test(object o) 13 { 14 Console.WriteLine("返回值:{0}", o); 15 } 16 }
上述代码将参数string在调用目标方法时隐式向上转换为Object。
3.返回类型的兼容性:如果调用一个方法,得到的返回值类型可能大于请求的类型,这是正常多态行为。同样,委托的返回类型可以小于它的目标方法的返回值类型即协变。
1 class Program 2 { 3 //委托返回object类型 4 delegate object NoReturnWithParameters(string o); 5 static void Main(string[] args) 6 { 7 NoReturnWithParameters noReturnWithParameters = new NoReturnWithParameters(Test); 8 object o = noReturnWithParameters("demo-ok"); 9 Console.WriteLine(o); 10 Console.ReadKey(); 11 } 12 //目标方法返回string类型 13 static string Test(string o) 14 { 15 return "返回值:" + o; 16 } 17 }
注意:标准事件模式的设计宗旨时再其使用公共基类EventArgs时应用逆变。例如,可以用两个不同的委托调用同一个方法,一个传递MouseEventArgs,另一个传递KeyEventArgs。
多播委托(+=&-=)
1 class Program 2 { 3 public delegate int MulticastInstance(int inputA, int inputB); 4 static void Main(string[] args) 5 { 6 MulticastInstance multicastInstance = Addition; 7 multicastInstance += new MulticastInstance(Reduce); 8 multicastInstance += new MulticastInstance(Multiply); 9 int result = multicastInstance(10, 5); 10 Console.WriteLine("最后执行得到的结果为:{0}", result); 11 Console.ReadKey(); 12 } 13 /// <summary> 14 /// 加法 15 /// </summary> 16 /// <param name="inputA"></param> 17 /// <param name="inputB"></param> 18 /// <returns></returns> 19 private static int Addition(int inputA, int inputB) 20 { 21 int result = inputA + inputB; 22 Console.WriteLine("Addition方法执行结果:{0}", result); 23 return result; 24 } 25 /// <summary> 26 /// 减法 27 /// </summary> 28 /// <param name="inputA"></param> 29 /// <param name="inputB"></param> 30 /// <returns></returns> 31 private static int Reduce(int inputA, int inputB) 32 { 33 int result = inputA - inputB; 34 Console.WriteLine("Reduce方法执行结果:{0}", result); 35 return result; 36 } 37 /// <summary> 38 /// 乘法 39 /// </summary> 40 /// <param name="inputA"></param> 41 /// <param name="inputB"></param> 42 /// <returns></returns> 43 private static int Multiply(int inputA, int inputB) 44 { 45 int result = inputA * inputB; 46 Console.WriteLine("Multiply方法执行结果:{0}", result); 47 return result; 48 } 49 /* 50 * 作者:Jonins 51 * 出处:http://www.cnblogs.com/jonins/ 52 */ 53 }
得到的结果如下:
委托模拟观察者
能用委托解决的问题,都可以用接口解决。但再下面的情形中,委托可能是比接口更好的选择:
1.接口内之定义一个方法
2.需要多播能力
3.订阅者需要多次实现接口
下面代码是委托的观察者模式,优点是解耦且符合开放封闭原则:
1 public class MulticastDelegates 2 { 3 public delegate int MulticastInstance(int inputA, int inputB); 4 ///