【C#】详解C#委托
目录结构:
- 委托语法
- 泛型委托
- 委托链
- lambda表达式
- 揭秘委托
- 类库中的委托
- 委托和反射
1.委托语法
本文会详细阐述委托的使用,以及实现,想必读者已经知道了函数编程中的回调机制,C#为回调机制提供了一种简便语法,这就是委托。
在使用委托之前需要使用delegate关键字进行声明:
比如,
delegate void MyDelegate();
上面定义了一个无参数,无返回的委托。定义好后,然后就可以声明委托对象。
MyDelegate myDelegate=new MyDelegate(SomeStaticMethod);
上面创建了一个委托对象,这个委托对象关联了一个方法参数。
C#为这种操作提供了一种糖语法,就是不需要构造委托对象,如下:
MyDelegate myDelegate=SomeStaticMethod;
示例:
class Program{delegate void MyDelegate();static void Main(string[] args){MyDelegate myDelegate = Method1;//委托静态方法Method1myDelegate += new Program().Method2;//委托实例方法Method2myDelegate.Invoke();//调用委托链上的所有方法 Console.ReadKey();}static void Method1() {Console.WriteLine("method1 invoked");}void Method2() {Console.WriteLine("method2 invoked");}}
2.泛型委托
C#允许泛型委托,目的是保证任何类型的对象都可以以类型安全的方式传给回调方法。
例如:
public delegate TResult CallMe<TResult,TKey,TValue>(TKey key,TValue value);
委托的每个泛型类型参数都可以标记为协变量和逆变量。
在这里介绍一下泛型类型参数的三种变量:
不变量(invariant):意味着泛型类类型参数不能更改。
逆变量(contravariant):意味着泛型类型参数可以从一个类更改为它的某个派生类,C#中使用in关键字标记逆变量形式的泛型类型参数。逆变量泛型类型参数T只能出现在输入位置,作为参数。
协变量(convariant):意味着泛型类型参数可以从一个类更改为它的某个基类,C#中使用out关键字标记协变量形式的泛型类型参数。逆变量泛型类型参数只能出现在输出位置,作为返回类型。
例如存在以下泛型类型委托:
public delegate TResult MyFunc<in T,out TResult>(T arg);
如果像下面定义一个委托变量:
MyFunc(Object,ArgumentException) fn1=null;
然后就可以将它转变为其他委托类型变量:
MyFunc(String,Exception) fn2=fn1;//转化 fn2("");//调用
3.委托链
C#中提供了一种委托链的操作,通过委托链可以关联多个方法。
C#在实现委托链的时候使用了组合设计模式。C#中使用Delegate类的Combine类来完成委托的链接,例如:
class Program{delegate Int32 Max(Int32 a, Int32 b);static void Main(string[] args){Max max1 = new Max(new Program().Method1);//定义委托max1Max max2 = new Max(new Program().Method2);//定义委托max2Delegate max = Delegate.Combine(max1, max2);//组合委托链Object obj= max.DynamicInvoke(12,13);//动态调用Console.WriteLine(obj);//13 Console.ReadLine();}Int32 Method1(Int32 a, Int32 b) {return a > b ? a : b;}Int32 Method2(Int32 a, Int32 b) {if (a > b) {return a;}return b;}}
上面我们使用Delegate的Combine方法合并为委托链,其实Delegate类还提供了许多操作方法,例如:
Delegate.remove(Delegate,Delegate);//从第一个委托链中移除第二个委托链中的委托 Delegate.DynamicInvoke(Object[]);//调用委托链中的所有方法 static Delegate CreateDelegate(Type type,MethodInfo method)//从给定的委托类型中,创建一个静态方法委托
在C#中还给Delegate重载了+=和-=操作符,可以更方便的操作委托链,比如:
Max max1 = new Max(new Program().Method1);//定义委托max1Max max2 = new Max(new Program().Method2);//定义委托max2max1 += max2;
4.lambda表达式
Lambda表达式是C#3.0才添加的功能,是为委托添加的糖语法,lambda表达式允许以内联的方式写回调方法的代码,而不需要单独写到方法中,例如:
delegate Int32 Max(Int32 a, Int32 b);static void Main(string[] args){Max m = null;m += (a, b) => {return a > b ? a : b;};//lambda表达式Int32 res= m(10,11);//调用方法 Console.WriteLine(res);Console.ReadLine();}
lambda表达式的大致格式为:(参数...)=>{方法主体}
通常我们在写线程的时候,我们都会定义一个WaitCallback委托,然后再关联方法,现在我们可以像如下这样:
ThreadPool.QueueUserWorkItem((obj) => {Console.WriteLine("Thread:" + Thread.CurrentThread.ManagedThreadId);});Console.ReadLine();
关于使用委托,这里有一个不成文规定,通常若是需要在回调方法写3行以上的代码就不用lambda表达式,而是重新定义一个方法,并为其分配名称。若小于等于3行代码,就可以使用lambda表达式。
例如:
//创建并初始化一个String数组String[] names = { "Jeff","Kristin","Aidan","Grant"};//获取只含有小写字母a的名称Char charTOFind='a';names = Array.FindAll(names, (name) => { return name.IndexOf(charTOFind) >= 0; });//将每个字符串的名称转化为大写names= Array.ConvertAll(names, (name) => { return name.ToUpper(); });//打印Array.ForEach(names,System.Console.WriteLine);
5.揭秘委托
到现在我们已经知道了如何定义委托,以及C#为委托提供的糖语法,接下来我们开始探讨一下委托究竟是什么,将如下的代码编译为二进制文件,
class Program{delegate Int32 MathAdd(Int32 a, Int32 b);static void Main(string[] args){MathAdd mathAdd = new MathAdd((Int32 a, Int32 b) => { return a + b; });Int32 result= mathAdd(3,2);Console.WriteLine(result);//5 Console.ReadKey();}}
然后我们使用ildasm.exe反编译应用程序得到il文件,就可以从CLR层面观察出C#的委托到底是怎么回事了。
通过这张图片我们可以清晰的观察出,IL代码生成了一个静态内部类MathAdd,然后该类派生于MulticastDelegate,到现在我们清楚了C#的委托本质上就是类。除此之外,该类提供了一个构造器方法,BeginInvoke方法,EndInvoke方法,Invoke方法。
6.类库中的委托
C#已经预先定义好了丰富的委托类型供开发人员使用,在MSCorLib.dll中,有将近50个委托类型,
例如:
public delegate void TryCode(object userData); public delegate void WaitCallback(object state); public delegate void TimerCallback(object state); public delegate void ContextCallback(object state);......
上面这些委托都有一个共同点,就是他们都是一样的,也就是说,只需要定义一个委托就可以了。
.NET Framework支持泛型,C#已经为我们定义好了17个Action委托,其中包含16个泛型委托:
public delegate void Action(); public delegate void Action<T>(T arg); public delegate void Action<T1,T2>(T1 arg1,T2 arg2); public delegate void Action<T1,T2,T3>(T1 arg1,T2 arg2,T3 arg3); ...... public delegate void Action<T1,.....,T16>(T1 arg1,......,T16 arg16);
除了Action委托,C#还为我们定义了Func委托,允许回调方法返回值:
public delegate TResult Func<TResult>(); public delegate TResult Func<T,TResult>(T arg); public delegate TResult Func<T1,T2,TResult>(T1 arg1,T2 arg2); public delegate TResult Func<T1,T2,T3,TResult>(T1 arg1,T2 arg2,T3 arg3); ...... public delegate TResult Func<T1,.....,T16,TResult>(T1 arg1,.....,T16 arg16);
上面列出了C#定义的Action和Func委托,如果涉及到委托最好是使用这些委托类型,而不是重新定义,这样可以减少程序集的大小。
但是如果涉及到ref或out参数,那么就只有自己定义了。
比如:
delegate void Bar(ref Int32 z);
7.委托和反射
通常情况下,使用委托都应该知道委托的原型,但是反射提供了另一种可能来使用委托。
在MethodInfo中提供了一个createDelegate方法,运行在编译器不知道委托的所有信息下提前创建委托。
public virtual Delegate CreateDelegate(Type delegateType);public virtual Delegate CreateDelegate(Type delegateType, object target);
在创建好Delegate对象后,就可以通过调用对象的DynamicInvoke方法来调用委托。
public Object DynamicInvoke(params Object[] args);
下面的展示了如何使用反射来调用委托:
class Program{static void Main(string[] args){//定义一个Func<Int32,Int32,Int32>类型的数据Type type=typeof(Func<Int32,Int32,Int32>);//获得Program实例的Max方法MethodInfo methodInfo = typeof(Program).GetMethod("Max", BindingFlags.Instance|BindingFlags.Public);//创建委托Delegate d = methodInfo.CreateDelegate(type, new Program());//调用Object res = d.DynamicInvoke(13, 12);Console.WriteLine(res);//13 Console.ReadKey();}public Int32 Max(Int32 a, Int32 b){return a > b ? a : b;}}
转载于:https://www.cnblogs.com/HDK2016/p/7771404.html
【C#】详解C#委托相关推荐
- 详解C#委托,事件与回调函数
.Net编程中最经常用的元素,事件必然是其中之一.无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等. "protected vo ...
- 委托与事件代码详解与(Object sender,EventArgs e)详解
委托与事件代码详解 using System; using System.Collections.Generic; using System.Text; namespace @Delegate //自 ...
- 基础拾遗------委托详解
基础拾遗: 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗---- ...
- java 委托机制_通过反射实现Java下的委托机制代码详解
简述 一直对Java没有现成的委托机制耿耿于怀,所幸最近有点时间,用反射写了一个简单的委托模块,以供参考. 模块API public Class Delegater()//空参构造,该类管理委托实例并 ...
- java委托机制教程_通过反射实现Java下的委托机制代码详解
简述 一直对java没有现成的委托机制耿耿于怀,所幸最近有点时间,用反射写了一个简单的委托模块,以供参考. 模块api public class delegater()//空参构造,该类管理委托实例并 ...
- 详解Objective-C中委托和协议
Objective-C委托和协议本没有任何关系,协议如前所述,就是起到C++中纯虚类的作用,对于"委托"则和协议没有关系,只是我们经常利用协议还实现委托的机制,其实不用协议也完全可 ...
- NEXT社区小课堂 | 第四课:dBFT 2.0详解 | 委托拜占庭容错:技术细节、挑战和前景...
NEXT社区 | 小课堂 由于近期NEXT社区加入很多新的小伙伴,有在校大学生,有对区块链感兴趣的传统企业从业者.为了更方便.更系统的让NEXT社区的伙伴们了解NEO的技术知识,因此我们开设了小课堂, ...
- Csharp委托详解
参考视频 c#教程 using System; using System.Collections.Generic; using System.Linq; using System.Text; name ...
- CSharp(C#)语言_委托和事件区别详解
委托和事件区别详解 委托和事件的概念 委托 事件 委托和事件的作用 委托 事件 委托和事件的区别 委托和事件的详细解答请看C#系列文章 委托和事件代码实践 委托 事件 总结 委托和事件的概念 委托 ...
最新文章
- linux coredump配置与调试
- 理解 Cinder 架构 - 每天5分钟玩转 OpenStack(45)
- 解决pip安装时速度慢的问题 镜像源(pip install -i [镜像源地址] [包名])
- PHP:10个不常见却非常有用的PHP函数
- Digix联合创始人:在接下来的12个月中 比特币将被称为真正的价值存储
- nginx 代理静态资源报 403
- Win7 x64 sp1安装orcale 10g
- MySQL(2)----DDL语句之增、删、改、查操作
- 【C语言视频教程完整版】从入门到进阶,适合C语言初学者计算机考研党考计算机二级大一大二学生学习观看~~~
- 软件项目管理的基本概念
- 我能想到最赛博朋克的事,就是为全世界的工程师雕刻一尊“虚拟分身”
- IDEA导入项目后如何运行
- xxampp 配置php_MAC下使用XMAPP配置php环境
- 计算机教育课题申请报告,课题结项申请报告
- xp系统计算机配置在哪里设置,XP系统电脑进行声卡设置的方法介绍
- YARN 作业执行流程
- 区块链:核心技术概览
- ubuntu终端英文乱码问题
- 微信小程序下载图片获取相册授权,保存视频或图片到手机相册
- ps快速去掉图中的字
热门文章
- c# 学习笔记 (1) 类型转换
- 模拟电路技术之基础知识(一)
- CTFshow 信息收集 web15
- [YTU]_1032( 统计出其中英文字母、数字、空格和其他字符的个数)
- setdiff--求两个集合的差
- 快速寻找满足条件的两个数
- 【Kaggle-MNIST之路】两层的神经网络Pytorch(四行代码的模型)
- 多线程生成随机数组+多线程快速排序(C++实现)
- Centos(阿里云) 安装python3.4以及pip3
- Luogu4926 倍杀测量者(二分答案+差分约束)