博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【C#】详解C#委托
阅读量:5773 次
发布时间:2019-06-18

本文共 6554 字,大约阅读时间需要 21 分钟。

目录结构:

contents structure
[+]

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;//委托静态方法Method1            myDelegate += new Program().Method2;//委托实例方法Method2            myDelegate.Invoke();//调用委托链上的所有方法            Console.ReadKey();        }        static void Method1() {            Console.WriteLine("method1 invoked");        }        void Method2() {            Console.WriteLine("method2 invoked");        }    }

2.泛型委托

C#允许泛型委托,目的是保证任何类型的对象都可以以类型安全的方式传给回调方法。

例如:

public delegate TResult CallMe
(TKey key,TValue value);

委托的每个泛型类型参数都可以标记为协变量和逆变量。

在这里介绍一下泛型类型参数的三种变量:
不变量(invariant):意味着泛型类类型参数不能更改。
逆变量(contravariant):意味着泛型类型参数可以从一个类更改为它的某个派生类,C#中使用in关键字标记逆变量形式的泛型类型参数。逆变量泛型类型参数T只能出现在输入位置,作为参数。
协变量(convariant):意味着泛型类型参数可以从一个类更改为它的某个基类,C#中使用out关键字标记协变量形式的泛型类型参数。逆变量泛型类型参数只能出现在输出位置,作为返回类型。

例如存在以下泛型类型委托:

public delegate TResult MyFunc
(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);//定义委托max1            Max max2 = new Max(new Program().Method2);//定义委托max2            Delegate 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);//定义委托max1            Max max2 = new Max(new Program().Method2);//定义委托max2            max1 += 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 arg);public delegate void Action
(T1 arg1,T2 arg2);public delegate void Action
(T1 arg1,T2 arg2,T3 arg3);......public delegate void Action
(T1 arg1,......,T16 arg16);

除了Action委托,C#还为我们定义了Func委托,允许回调方法返回值:

public delegate TResult Func
();public delegate TResult Func
(T arg);public delegate TResult Func
(T1 arg1,T2 arg2);public delegate TResult Func
(T1 arg1,T2 arg2,T3 arg3);......public delegate TResult Func
(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
类型的数据 Type type=typeof(Func
); //获得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; } }

 

转载地址:http://vbaux.baihongyu.com/

你可能感兴趣的文章
Microsoft发布了Azure Bot Service和LUIS的GA版
查看>>
Google发布Puppeteer 1.0
查看>>
.NET开源现状
查看>>
可替换元素和非可替换元素
查看>>
2016/08/25 The Secret Assumption of Agile
查看>>
(Portal 开发读书笔记)Portlet间交互-PortletSession
查看>>
搭建vsftpd服务器,使用匿名账户登入
查看>>
AMD改善Linux驱动,支持动态电源管理
查看>>
JAVA中循环删除list中元素的方法总结
查看>>
Java虚拟机管理的内存运行时数据区域解释
查看>>
人人都会深度学习之Tensorflow基础快速入门
查看>>
ChPlayer播放器的使用
查看>>
js 经过修改改良的全浏览器支持的软键盘,随机排列
查看>>
Mysql读写分离
查看>>
Oracle 备份与恢复学习笔记(5_1)
查看>>
Oracle 备份与恢复学习笔记(14)
查看>>
分布式配置中心disconf第一部(基本介绍)
查看>>
Scenario 9-Shared Uplink Set with Active/Active uplink,802.3ad(LACP)-Flex-10
查看>>
UML类图中的六种关系
查看>>
探寻Interpolator源码,自定义插值器
查看>>