【问题标题】:Why do we need the "event" keyword while defining events?为什么我们在定义事件时需要“event”关键字?
【发布时间】:2010-06-12 12:55:38
【问题描述】:

我不明白为什么我们在定义事件时需要“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 关键字,任何可以使用类对象访问该事件的人都可以将其设置为 NULL,例如 objClass.SelectedIndexChanged = null。这将使您的底层代码崩溃。 event 关键字强制用户使用 += 分配类似于委托的内容。

标签: c# .net


【解决方案1】:

类字段事件和委托类型的公共字段看起来相似,但实际上却大不相同。

事件本质上类似于属性——它是一对添加/删除方法(而不是属性的获取/设置)。当您声明一个类似字段的事件(即您自己没有指定添加/删除位的事件)时,会创建一个公共事件和一个私有支持字段。这使您可以私下引发事件,但允许公开订阅。使用公共委托字段,任何人都可以删除其他人的事件处理程序,自己引发事件等 - 这是一场封装灾难。

有关活动(和代表)的更多信息,请阅读我的article on this topic。 (在某些时候,我需要为 C# 4 更新它,它会非常轻微地改变类似字段的事件。但它的要点仍然是正确的。)

【讨论】:

  • 这比MSDN官方的一行解释要好一千倍:'event关键字用于声明发布者类中的事件'
  • @cowlinator "嗯...是的。这里的地板是用地板做的。"
【解决方案2】:

event 关键字做了 3 件不同的事情:

  1. 您可以在接口中定义事件,即使您不能在接口中定义常规字段。
  2. 它将=() 运算符(赋值和调用)的可见性更改为私有,这样只有包含类才能调用事件或覆盖其中包含的所有方法。 -=+= 运算符仍然可以从定义它的类外部对事件调用(它们会获得您在事件旁边编写的访问修饰符)。
  3. 您还可以覆盖 -=+= 在事件上的行为方式。

【讨论】:

  • 你 > MSDN。谢谢。
【解决方案3】:

其他答案都很好;我只是想补充一些其他的想法。

您的问题是“当我们有委托类型的字段时,为什么我们需要事件?”我会扩展这个问题:如果你有委托类型的字段,为什么你需要方法、属性、事件、实例构造函数或终结器?为什么除了包含类型中的值和委托的字段之外,您还需要任何东西?为什么不直接说

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

?

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

【讨论】:

  • 哇!它提醒了我为什么喜欢 c#!在我使用过的所有语言中,它在紧凑性、灵活性和可读语义方面取得了适当的平衡。唯一可比较的语言是 Object Pascal。
  • @ATL_DEV:这是有原因的。 C# 语言的架构师 Anders Hejlsberg 之前是 Delphi 的架构师,这是一种基于 Object Pascal 的语言。
【解决方案4】:

它是部分需要的,因为如果您省略 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(...);
   }       
}

...但那是一大堆代码,只是为了(或多或少)做类似字段的事件已经给我们的东西。

【讨论】:

  • 设计师之类的东西也更难使用……你基本上会依赖方法的命名约定,而不是有公共元数据说“这是一个事件”。跨度>
【解决方案5】:

与委托字段相比,事件具有明显的优势。与字段相比,事件可以在接口中定义,为代码添加抽象,更重要的是:事件只能从定义类内部调用。在您的情况下,任何人都可以调用该事件,可能会破坏您的代码。

更多信息请参见this blog post

【讨论】:

  • 您不应该真正比较事件和委托 - 比较事件和具有委托类型的公共字段。不,框架要求事件具有该签名。您可以创建任何您喜欢的委托类型的事件。
【解决方案6】:

delegate 是一个引用类型。它继承MulticastDelegateevent 是一个修饰符。 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
        }
    }
}

【讨论】:

    【解决方案7】:

    使用网站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 等许多其他关键字相同。所以它实际上只是语法糖,仅此而已。

    为了好玩,请尝试使用 sharplab.io 反编译其他关键字来查看。这是一次很棒的学习经历。

    【讨论】:

      猜你喜欢
      • 2014-05-29
      • 2016-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-10
      相关资源
      最近更新 更多