我不明白为什么在定义事件时我们需要“event”关键字,当我们可以在不使用“event”关键字的情况下执行相同的操作,只需使用委托。
e.g。
public delegate void CustomEventHandler(int a, string b);
public event CustomEventHandler customEvent;
customEvent += new CustomEventHandler(customEventHandler);
customEvent(1,"a"); // Raising the event
如果我从第二行删除“event”关键字,那么我也可以通过调用委托来引发事件。任何人都可以告诉我为什么需要这个事件关键字?
答案 0 :(得分:129)
类似于字段的事件和委托类型的公共字段看起来类似,但实际上非常不同。
事件基本上类似于属性 - 它是一对添加/删除方法(而不是属性的get / set)。当您声明一个类似字段的事件(即您没有自己指定添加/删除位的事件)时,会创建一个公共事件和一个私有支持字段。这允许您私下引发事件,但允许公共订阅。使用公共委托字段,任何人可以删除其他人的事件处理程序,自己引发事件等等 - 这是一个封装灾难。
有关活动(和代表)的更多信息,请阅读我的article on this topic。 (在某些时候,我需要为C#4更新它,它会稍微改变类似字段的事件。但它的要点仍然是正确的。)
答案 1 :(得分:30)
event关键字有3种不同的东西:
=
和()
运算符(赋值和调用)的可见性更改为private,以便只有包含类可以调用事件或覆盖其中包含的所有方法。仍然可以在定义它的类之外的事件上调用-=
和+=
运算符(它们获取您在事件旁边写入的访问修饰符)。-=
和+=
对事件的行为方式。答案 2 :(得分:22)
其他答案都很好;我想补充一些其他想法。
你的问题是“当我们有代表类型的字段时,为什么我们需要事件?”我会扩展这个问题:如果你有委托类型的字段,为什么还需要方法,属性,事件,实例构造函数或终结器?为什么除了包含类型中的值和委托的字段之外,还需要任何?为什么不直接说
class C
{
private int z;
public readonly Func<int, int> M = (int x)=>{ return x+z; }
// ... and so on
}
您没有需要方法,属性或事件。我们为您提供这些内容,因为方法,属性和事件设计模式非常重要且有用,并且应该有一种标准的,有文档记录的,清晰的方式来实现它们。
答案 3 :(得分:8)
部分需要,因为如果省略event
关键字,则会破坏封装。如果它只是一个公共多播委托,任何人都可以调用它,将其设置为null或篡改它。如果名为MailNotifier
的类存在并且它有一个名为MailReceived
的事件,则其他类型无法通过调用mailNotifier.MailReceived()
来触发该事件;
另一方面,您只能插入并调用定义它的类型的'field like'事件。
如果您想将您的事件调用保密,那么就没有什么可以阻止您做这样的事情:
public class MyClassWithNonFieldLikeEvent
{
private CustomEventHandler m_delegate;
public void Subscribe(CustomEventHandler handler)
{
m_delegate += handler;
}
public void Unsubscribe(CustomEventHandler handler)
{
m_delegate -= handler;
}
private void DoSomethingThatRaisesEvent()
{
m_delegate.Invoke(...);
}
}
...但这只是一大堆代码,只是(或多或少)做了类似字段的事件已经给我们带来的。
答案 4 :(得分:3)
与委托字段相比,事件具有明显的优势。事件可以在接口中定义,与字段形成对比,为代码添加抽象,更重要的是:事件只能从定义类内部调用。在您的情况下,任何人都可以调用该事件,可能会破坏您的代码。
有关详细信息,请参阅this blog post。
答案 5 :(得分:1)
delegate是引用类型。它继承了MulticastDelegate。 event是修饰符。 event是委托的特殊修饰符。它修改了某些功能/方法的可访问性,例如调用方法。通过修饰符事件修改后,委托实例成为新概念“事件”。因此,Event只是一个修改的委托。您不能在定义事件的类之外直接更改引用或调用事件,但是可以更改引用或调用普通的委托实例。事件提供了额外的保护,因此事件具有更多的安全功能。 当您在定义事件的类之外时,可以对事件执行两种操作:“ + =”和“-=”。 但是您可以访问普通委托实例的所有公共字段,属性,方法等。 这是一个例子:
namespace DelegateEvent
{
//the following line behave as a class. It is indeed a reference type
public delegate void MyDelegate(string inputs);
//The following line is illegal. It can only be an instance. so it cannot be directly under namespace
//public event MyDelegate MyEvent;
public class MyClassA
{
public event MyDelegate MyEventA;
public MyDelegate MyDelegateA;
System.Threading.ManualResetEvent MyResetEvent = new System.Threading.ManualResetEvent(false);
public void TryToDoSomethingOnMyDelegateA()
{
if (MyDelegateA != null)
{
//User can assecc all the public methods.
MyDelegateA("I can invoke detegate in classA"); //invoke delegate
MyDelegateA.Invoke("I can invoke detegate in classA"); //invoke delegate
IAsyncResult result = MyDelegateA.BeginInvoke("I can invoke detegate in classA", MyAsyncCallback, MyResetEvent); //Async invoke
//user can check the public properties and fields of delegate instance
System.Reflection.MethodInfo delegateAMethodInfo = MyDelegateA.Method;
MyDelegateA = testMethod; //reset reference
MyDelegateA = new MyDelegate(testMethod); //reset reference
MyDelegateA = null; //reset reference
MyDelegateA += testMethod; //Add delegate
MyDelegateA += new MyDelegate(testMethod); //Add delegate
MyDelegateA -= testMethod; //Remove delegate
MyDelegateA -= new MyDelegate(testMethod); //Remove delegate
}
}
public void TryToDoSomethingOnMyEventA()
{
if (MyEventA != null)
{
MyEventA("I can invoke Event in classA"); //invoke Event
MyEventA.Invoke("I can invoke Event in classA"); //invoke Event
IAsyncResult result = MyEventA.BeginInvoke("I can invoke Event in classA", MyAsyncCallback, MyResetEvent); //Async invoke
//user can check the public properties and fields of MyEventA
System.Reflection.MethodInfo delegateAMethodInfo = MyEventA.Method;
MyEventA = testMethod; //reset reference
MyEventA = new MyDelegate(testMethod); //reset reference
MyEventA = null; //reset reference
MyEventA += testMethod; //Add delegate
MyEventA += new MyDelegate(testMethod); //Add delegate
MyEventA -= testMethod; //Remove delegate
MyEventA -= new MyDelegate(testMethod); //Remove delegate
}
}
private void MyAsyncCallback(System.IAsyncResult result)
{
//user may do something here
}
private void testMethod(string inputs)
{
//do something
}
}
public class MyClassB
{
public MyClassB()
{
classA = new MyClassA();
}
public MyClassA classA;
public string ReturnTheSameString(string inputString)
{
return inputString;
}
public void TryToDoSomethingOnMyDelegateA()
{
if (classA.MyDelegateA != null)
{
//The following two lines do the same job --> invoke the delegate instance
classA.MyDelegateA("I can invoke delegate which defined in class A in ClassB");
classA.MyDelegateA.Invoke("I can invoke delegate which defined in class A in ClassB");
//Async invoke is also allowed
//user can check the public properties and fields of delegate instance
System.Reflection.MethodInfo delegateAMethodInfo = classA.MyDelegateA.Method;
classA.MyDelegateA = testMethod; //reset reference
classA.MyDelegateA = new MyDelegate(testMethod); //reset reference
classA.MyDelegateA = null; //reset reference
classA.MyDelegateA += testMethod; //Add delegate
classA.MyDelegateA += new MyDelegate(testMethod); //Add delegate
classA.MyDelegateA -= testMethod; //Remove delegate
classA.MyDelegateA -= new MyDelegate(testMethod); //Remove delegate
}
}
public void TryToDoSomeThingMyEventA()
{
//check whether classA.MyEventA is null or not is not allowed
//Invoke classA.MyEventA is not allowed
//Check properties and fields of classA.MyEventA is not allowed
//reset classA.MyEventA reference is not allowed
classA.MyEventA += testMethod; //Add delegate
classA.MyEventA += new MyDelegate(testMethod); //Add delegate
classA.MyEventA -= testMethod; //Remove delegate
classA.MyEventA -= new MyDelegate(testMethod); //Remove delegate
}
private void testMethod(string inputs)
{
//do something here
}
}
}