.net 为什么在定义事件时需要“event”关键字?

ngynwnxp  于 2022-12-27  发布在  .NET
关注(0)|答案(7)|浏览(165)

我不明白为什么我们在定义事件时需要event关键字,而我们可以不使用event关键字,只使用委托来做同样的事情。
例如:

public delegate void CustomEventHandler(int a, string b);
public event CustomEventHandler customEvent;
customEvent += new CustomEventHandler(customEventHandler);
customEvent(1,"a"); // Raising the event

在这里,如果我从第二行删除event关键字,那么我还可以通过调用委托来引发事件。
有人能告诉我为什么需要这个event关键字吗?

lskq00tm

lskq00tm1#

类似字段的事件和委托类型的公共字段看起来很相似,但实际上有很大的不同。
事件基本上类似于属性-它是一对添加/删除方法(而不是属性的get/set)。当声明类似字段的事件时(即你自己不指定添加/删除位)一个公共事件和一个私有支持字段被创建。这允许你私下引发事件,但允许公共订阅。使用公共委托字段,* 任何人 * 都可以删除其他人的事件处理程序,自己引发事件,等等--这是一场封装灾难。
关于事件(和委托)的更多信息,请阅读我的article on this topic。(在某些时候,我需要为C# 4更新它,它对类似字段的事件做了很小的修改。尽管它的要点仍然是正确的。)

syqv5f0l

syqv5f0l2#

event关键字有3个不同的作用:
1.可以在接口中定义事件,即使不能在接口中定义常规字段。
1.它将=()操作符(赋值和调用)的可见性更改为private,以便只有包含类才能调用事件或覆盖其中包含的所有方法。-=+=操作符仍然可以从定义事件的类之外对事件进行调用(它们获得您在事件旁边编写的访问修饰符)。
1.您还可以覆盖-=+=对事件的行为方式。

6kkfgxo0

6kkfgxo03#

其他答案都很好;我只是想补充一些其他的思考。
您的问题是“当我们有委托类型的字段时,为什么还需要事件?”我将扩展该问题:如果你有委托类型的字段,为什么你需要方法、属性、事件、示例构造函数或终结器?除了包含值和委托的字段之外,你为什么还需要其他任何东西?为什么不直接说

class C
{
    private int z;
    public readonly Func<int, int> M = (int x)=>{ return x+z; }
    // ... and so on
}


你不需要方法、属性或事件,我们给予你这些东西是因为方法、属性和事件设计模式是重要和有用的,应该有一个标准的、文档化的、清晰的方法来在语言中实现它们。

pgccezyw

pgccezyw4#

之所以需要它,部分原因是如果省略了event关键字,它会破坏封装。如果它只是一个公共多播委托,任何人都可以调用它,将它设置为null或篡改它。如果一个名为MailNotifier的类存在,并且它有一个名为MailReceived的事件,那么其他类型通过调用mailNotifier.MailReceived()来触发该事件是没有意义的;
另一方面,您只能从定义它的类型中干预和调用“类似字段”的事件。
如果你想让你的事件调用保持私有,没有什么可以阻止你做这样的事情:

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(...);
   }       
}

......但这是一个完整的代码负载,只是(或多或少)做什么场类事件已经给予我们。

u0sqgete

u0sqgete5#

与委托字段相比,事件具有明显的优势。事件可以在接口中定义,这与字段相反,增加了代码的抽象性,更重要的是:事件只能从定义类内部调用。在您的情况下,任何人都可以调用事件,可能会破坏您的代码。
有关详细信息,请参见this blog post

wnavrhmk

wnavrhmk6#

delegate是一个引用类型,它继承了MulticastDelegate,event是一个修饰符,event是一个委托的特殊修饰符,它修改了一些函数/方法的可访问性,比如Invoke方法,被修饰符事件修改后,一个委托示例变成了一个新的概念“事件”,所以事件只是一个修改过的委托。不能直接更改引用或调用定义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
        }
    }
}
lymnna71

lymnna717#

使用网站sharplab.io,您实际上可以反编译“event”关键字的作用。
例如下面的程序:

using System;
using System.ComponentModel;
public class C {
    
    public event EventHandler TestChanged;
    
    public void M() {
    }
}

反编译为以下内容:

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
public class C
{
    [CompilerGenerated]
    private EventHandler m_TestChanged;

    public event EventHandler TestChanged
    {
        [CompilerGenerated]
        add
        {
            EventHandler eventHandler = this.TestChanged;
            while (true)
            {
                EventHandler eventHandler2 = eventHandler;
                EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value);
                eventHandler = Interlocked.CompareExchange(ref this.TestChanged, value2, eventHandler2);
                if ((object)eventHandler == eventHandler2)
                {
                    break;
                }
            }
        }
        [CompilerGenerated]
        remove
        {
            EventHandler eventHandler = this.TestChanged;
            while (true)
            {
                EventHandler eventHandler2 = eventHandler;
                EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value);
                eventHandler = Interlocked.CompareExchange(ref this.TestChanged, value2, eventHandler2);
                if ((object)eventHandler == eventHandler2)
                {
                    break;
                }
            }
        }
    }

    public void M()
    {
    }
}

所以你可以写和上面一样的代码,只是很罗嗦,容易出错。event关键字会帮你处理这个问题。和其他很多关键字一样,比如async等等。所以它只是语法上的糖衣,仅此而已。
为了好玩,可以尝试使用www.example.com反编译其他关键字sharplab.io。这是一个很好的学习经验。

相关问题