一、委托
1、delegata
1.1 举例
生活中,如果如果我们需要打官司,在法庭上是由律师为我们辩护的,然而律师真真执行的是当事人的陈词,这时候律师就是一个委托对象,当事人委托律师这个对象去帮自己辩护。
然而C#中委托的概念也就好比律师对象(从中可以得出委托是一个类,,因为只有类才有对象的概念,从而也体现了C#是面向对象的语言)。
1.2 C#委托是什么
C#中的委托相当于C++中的函数指针(如果之前学过C++就知道函数指针是个什么概念的了),函数指针是用指针获取一个函数的入口地址,然后通过这个指针来实现对函数的操作。
两者是有区别的:委托是面向对象的,类型安全的,是引用类型(开始就说了委托是个类),所以在使用委托时首先要 定义——>声明——>实例化——>作为参数传递给方法——>使用委托。
主要用途是三个:1)函数回调;2)传递方法;3)事件机制。
下面就具体看下如何使用委托的:
1.3 定义
委托是指具有相同函数签名(返回类型相同,参数类型、参数顺序及参数个数相同)的函数或方法的抽象,关键字为delegate。
delegate void Mydelegate(type1 para1,type2 para2);
比定义一个方法就多了一个 关键字delegate
【从编译结果来看:委托实际上是一个类,该类派生于MulticastDelegate类】
1.4 声明
Mydelegate d;
1.5 实例化
d =new Mydelegate(obj.InstanceMethod);(把一个方法传递给委托的构造器),
前面三步就好比构造一个律师对象,方法InstanceMethod好比是当事人
1.6 作为参数传递给方法
MyMethod(d);(委托实现的是 把方法作为参数传入到另一个方法,委托就是一个包装方法的对象)
1.7 在方法中使用委托
MyMethod方法好比是法官,MyMethod方法先调用委托,委托在调用方法InstanceMethod,这个过程就如法官向律师问话,然后律师之前肯定向当事人了解了案件的情况。
C#委托中好比是律师,真真诉说案情的是当事人(真真被调用的是实例方法InstanceMethod)
MyMethod方法的定义如下:
private void MyMethod(Mydelegate mydelegate)
{
// 使用委托
mydelegat(arg1,arg2);
}
1.8 使用总结
委托是个特殊的类,我定义了方法的类型,(就像int定义了数字类型一样,当用一个方法实例化委托对象时,这个委托就代表一个方法,这个方法的类型就是委托类型),我可以将方法当做另一个方法的参数来进行传递,使得程序更容易扩展。
简单理解:委托对象的变量代替方法名
1.9 C#中为什么要使用委托
传统的面向过程编程,很多方法中就一两行代码不一样;改造为面向对象编程,传递一个方法对象【委托对象】给函数。
引入委托后,编程人员可以把方法的引用封装在委托对象中(把过程的调用转化为对象的调用,充分体现了委托加强了面向对象编程的思想。),然后把委托对象传递给需要引用方法的代码,这样在编译的过程中我们并不知道调用了哪个方法,这样一来,C#引入委托机制后,使得方法声明和方法实现的分离,充分体现了面向对象的编程思想。
1.10 委托本质
当我们在类中像下面这样定义一个委托时:
public delegate void DelegateTest(int parm);
编译器把我们定义的委托类型编译成一个下面这样的类:
Public class DelegateTest: System.MulticastDelegate { public DelegateTest(Object object, IntPtr method); public virtual Void Invoke(int32 parm); //两个异步方法 public virtual IAsyncResult BeginInvoke(Int32 parm, AsyncCallback callback, Object object ); public virtual void EndInvoke(IAsyncResult result); }
1.11 C#委托链
委托链就是一个委托,代表了多个方法。
通过调用GetInvocationList方法来返回一个委托数组,这样就可以通过遍历委托数组中的每个委托来通知委托的调用过程,这样就可以对委托链的调用进行更多的控制的。
2、Action<> 与Func<>
参考:C#委托的介绍(delegate、Action、Func、predicate)
2.1 delegate
public delegate int TestDelegate(int x, int y);
2.2 Action
Action是无返回值的泛型委托。 Action<T>
Action 表示无参,无返回值的委托
Action<int,string> 表示有传入参数int,string无返回值的委托
2.3 Func
Func是有返回值的泛型委托 ,返回值始终在最后一个类型参数中指定。
Func<int> 表示无参,返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
2.4 predicate
predicate 是返回bool型的泛型委托
predicate<int> 表示传入参数为int 返回bool的委托。
2.5 四者之间的区别
Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型
Action至少0个参数,至多16个参数,无返回值,
Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void
Predicate至少1个参数,至多1个参数,返回值固定为bool
二、Event事件
参考:事件揭秘
- 举例:
当一个人生日快到的时候,这时候就触发了生日事件的,此时过生日的人就是触发生日事件的对象,然后有些关系你的朋友就会对这个事件进行关注,一旦这个事件触发, 他们就可能会陪你一起庆祝生日,然后送礼物给你【这些就是事件触发后的动作】。
使用C#语言进行编码也是为了用代码帮助我们完成现实生活中的事情的,所以当然也就必须有事件来解决生活中发生事情的情况了。
- C#事件是什么:
事件其实就是委托(确切的说事件就是委托链),我们定义的事件除了使用event关键字外,还用到了一个委托类型,然而委托是一个类,类肯定就有属性字段的,
可以把事件理解为委托的一个属性,属性的返回值是一个委托类型。说事件是委托的一个属性,是有根据的,我们通过中间语言代码可以知道编译器是如何去解释我们定义的事件的。
//定义一个委托类型,用于指定事件触发时被调用的方法类型 public delegate void BirthDayEventHandle(object sender, BirthdayEventArgs e); // 定义生日事件 public event BirthDayEventHandle BirthDayEvent;
我们经常使用的是Net类库中定义好的事件,也可以自定义事件。
自定义事件实现过程主要是:定义触发对象的事件源(指的是谁过生日)->定义关注你生日事件的朋友对象-> 方法登记对事件的关注【委托链增加朋友对象的动作】-》事件源触发事件,当事件触发时通知登记的方法被调用。
【事件底层是操作系统的消息机制】
- 点击按钮时触发Click事件背后发生的事情
我们点击按钮后,我们后台代码中的Click方法就会执行。
事件肯定是调用了的, 只是不是我们代码中调用,而是Butoon控件的内部代码里面调用了事件,而导致委托封装的Click方法而被调用。
需要通过调试和反射工具去查看内部原理。
VS为我们自动创建了一个Button对象并实例化,设置了它的属性并通过 this.button1.Click += new System.EventHandler(this.button1_Click); 这行代码把 button1_Click注册对Click事件的关注,
事件的调用代码在哪里呢?
断点调试进 button1_Click方法,查看调用堆栈【在调试->窗口里面】
若这里显示是这样的,则在调用堆栈里面右键,显示外部代码
- 事件调用
发现在调用button1_Click方法之前要执行Control.OnClick(System.EventArgs e)方法的,然后我们用反射工具去查看下Control.OnClick(System.Eventrgs e)方法中具体有什么样的代码
首先从Events委托集合中取出委托,如果Click事件(委托)实例化了的话,此时就不为空,此时就会调用委托——handler(this, e),我们知道之前我们通过 this.button1.Click += new System.EventHandler(this.button1_Click);代码实例化了委托事件,所以此时被EventHandler封装的button1_Click方法就会执行。
- 事件注册
而Controller的Click事件:
前面说了: this.button1.Click += new System.EventHandler(this.button1_Click);这行代码把 button1_Click注册对Click事件的关注,
即base.Events中有button1_Click的委托了;在执行Control.OnClick里面,实际就会调用委托了的事件handler(this, e)。