1.与C++的函数指针进行简单类比
仅仅是个人理解,为涉及对底层原理的探究,主要是为了促进对委托和事件的记忆和理解。
该部分参考自:面试官:事件是以特殊方式声明的委托字段吗?(C#事件、委托详解) - 知乎
一般来说可以这样类比:
1)delegate 相当于定义一个函数类型(声明函数的返回类型和形参)。
2)event 用于修饰 delegate定义后的函数类型的函数指针(回调函数指针)。
3)不用event也能修饰 delegate定义后的函数类型的函数指针,但加上了event会使这个函数指针更加安全。
4)相对函数指针来说,委托更安全。
//C# Code
public class MyEvent
{
public delegate void MyFunc(int i);
public MyFunc DeleFunc;//不加event修饰符的委托
public event MyFunc EventFunc;//加event修饰符的委托
void TestMyEvent(int i)
{
Debug.Log(i);
}
void main()
{
EventFunc+=TestMyEvent;
DeleFunc += TestMyEvent;
EventFunc(1);
DeleFunc(2);
}
}
接下来用C++把上面的功能实现,当然C++里的函数指针可等价于不加event修饰符的委托情况。
//C++ Code
void TestMyEvent(int i){
cout<<i<<endl;
}
int main() {
using MyFunc=void(int i);//等价public delegate void MyFunc(int i);
MyFunc* DeleFunc;//等价public MyFunc DeleFunc;
DeleFunc=TestMyEvent;//等价DeleFunc += TestMyEvent;
DeleFunc(1);
return 0;
}
当然,将事件类比成函数指针还不太对,因为其可以用个+=和-=运算符等等,更确切说:
delegate用于定义一个类(类里包含一个函数链表),而event用于修饰delegate定义出的函数类所创建的对象。
2.加上event与不加event的委托的区别
event对一些功能做了封装和权限控制,使得其用起来会更安全。
毕竟event是采用了观察者模式,其分为了订阅者跟发布者的权限。订阅者只能注册事件,而发布者才有权限去控制是否去通知订阅者。
1)区别1:event修饰的委托在类外无法使用=运算符
通过这个特点可以预防直接对委托赋值一个null而导致整个委托链全都清空。
public class MyEvent
{
public delegate void MyFunc(int i);
public MyFunc DeleFunc;//不加event修饰符,为普通委托
public event MyFunc EventFunc;//加event修饰符,为事件
}
public class TestEvent
{
void myFync(int i)
{
Debug.Log(i);
}
MyEvent myEvent = new MyEvent();
void UseDelegate()//普通的委托可以使用=运算符
{
MyEvent myEvent = new MyEvent();
myEvent.DeleFunc += myFync;
myEvent.DeleFunc += myFync;
myEvent.DeleFunc = null;//直接将前面添加进委托的函数全都清空了
myEvent.DeleFunc += myFync;
myEvent.DeleFunc(1);//只输出1
}
void UseEvent()
{
myEvent.EventFunc += myFync;
myEvent.EventFunc += myFync;
myEvent.EventFunc += null;//event无法使用=运算符,不会将前面添加进委托的函数清空覆盖
myEvent.EventFunc += myFync;
myEvent.EventFunc(1);//输出1 1 1
}
}
2)区别2:event无法在类外直接调用Invoke
这是观察者模式最重要的一个体现,将事件的发布功能交给了发布者去控制。
这时候要想在其他类中触发事件需要在发布者类中提供一个发布的入口。
public class MyEvent
{
public delegate void MyFunc(int i);
public MyFunc DeleFunc;//不加event修饰符,为普通委托
public event MyFunc EventFunc;//加event修饰符,为事件
public void Notice(inti)//在发布者中暴露一个事件发布的功能接口
{
EventFunc.Invoke(i);
}
}
public class TestEvent
{
void myFync(int i)
{
Debug.Log(i);
}
MyEvent myEvent = new MyEvent();
void UseDelegate()//普通的委托可以使用=运算符
{
MyEvent myEvent = new MyEvent();
myEvent.DeleFunc += myFync;
myEvent.DeleFunc += myFync;
myEvent.DeleFunc.Invoke(2);//事件可以在类外直接调用,输出2 2
}
void UseEvent()
{
myEvent.EventFunc += myFync;
myEvent.EventFunc += myFync;
//myEvent.EventFunc.Invoke(2);//将报错
myEvent.Notice(i);//外部类需要通过发布者提供的暴露的接口去发布事件
}
}
3.其他的补充
参考自:【C#】委托应用总结
1)为了减少函数签名的声明,C#提供了几种常见的委托类型:
Action、Func、predicate。
2)委托可跟lambda表达式配合生成匿名方法:
delegate void TestDelegate(string myName);
TestDelegate d2 = delegate(string name)
{
Console.WriteLine("Hello,{0}!", name);
};
d2(“Test”);