代码之家  ›  专栏  ›  技术社区  ›  Sean Chambers

代表和活动之间有什么区别?

  •  374
  • Sean Chambers  · 技术社区  · 17 年前

    代表和活动之间有什么区别?不是两者都包含对可以执行的函数的引用吗?

    11 回复  |  直到 14 年前
        1
  •  332
  •   mmcdole    17 年前

    事件 声明为 代表 例子此保护可防止代理的客户端重置代理及其调用列表,并且只允许在调用列表中添加或删除目标。

        2
  •  142
  •   Cyril faby    4 年前

    为了理解差异,你可以看这两个例子

    带有委托的示例(在这种情况下,是一个Action,这是一种不返回值的委托)

    public class Animal
    {
        public Action Run {get; set;}
    
        public void RaiseEvent()
        {
            if (Run != null)
            {
                Run();
            }
        }
    }
    

    要使用委托,您应该这样做:

    Animal animal= new Animal();
    animal.Run += () => Console.WriteLine("I'm running");
    animal.Run += () => Console.WriteLine("I'm still running") ;
    animal.RaiseEvent();
    

    这段代码运行良好,但可能存在一些弱点。

    例如,如果我写下:

    animal.Run += () => Console.WriteLine("I'm running");
    animal.Run += () => Console.WriteLine("I'm still running");
    animal.Run = () => Console.WriteLine("I'm sleeping") ;
    

    在最后一行代码中,我覆盖了前面的行为,只缺少一个 + (我用过 = 而不是 += )

    另一个弱点是,每个使用你的 Animal 类可以直接调用委托。例如, animal.Run() animal.Run.Invoke() 在动物类之外有效。

    为了避免这些弱点,你可以使用 events c。

    你的动物类将以这种方式改变:

    public class ArgsSpecial : EventArgs
    {
        public ArgsSpecial (string val)
        {
            Operation=val;
        }
    
        public string Operation {get; set;}
    } 
    
    public class Animal
    {
        // Empty delegate. In this way you are sure that value is always != null 
        // because no one outside of the class can change it.
        public event EventHandler<ArgsSpecial> Run = delegate{} 
            
        public void RaiseEvent()
        {  
             Run(this, new ArgsSpecial("Run faster"));
        }
    }
    

    调用事件

     Animal animal= new Animal();
     animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
     animal.RaiseEvent();
    

    差异:

    1. 您使用的不是公共属性,而是公共字段(使用事件,编译器保护您的字段免受不必要的访问)
    2. 事件不能直接分配。在这种情况下,它不会导致我之前在覆盖行为时显示的错误。
    3. 类之外的任何人都不能引发或调用该事件。例如, 动物。运行() 动物。快跑。调用() 在Animal类之外无效,并将产生编译器错误。
    4. 事件可以包含在接口声明中,而字段不能

    笔记:

    事件处理程序被声明为以下委托:

    public delegate void EventHandler (object sender, EventArgs e)
    

    它接受一个发送方(Object类型)和事件参数。如果发送方来自静态方法,则为null。

    此示例使用 EventHandler<ArgsSpecial> ,也可以用 EventHandler 相反。

    参考 here 有关事件处理程序的文档

        3
  •  111
  •   Robert Harvey    16 年前

    除了句法和操作属性外,还有语义差异。

    从概念上讲,委托是功能模板;也就是说,它们表达了一个函数必须遵守的契约,才能被视为委托的“类型”。

    事件代表。..好吧,事件。它们的目的是在发生事情时提醒某人,是的,它们遵守委托定义,但它们不是一回事。

    即使它们完全相同(在语法和IL代码中),语义上的差异仍然存在。一般来说,我更喜欢为两个不同的概念命名,即使它们以相同的方式实现(这并不意味着我喜欢两次使用相同的代码)。

        4
  •  38
  •   Pang Ajmal PraveeN    7 年前

    这里还有一个很好的链接可以参考。 http://csharpindepth.com/Articles/Chapter2/Events.aspx

    简而言之,本文的结论是——事件是对委托的封装。

    引用文章:

    假设事件在C#/中不作为一个概念存在。网。另一个类将如何订阅事件?三种选择:

    1. 公共委托变量

    2. 由属性支持的委托变量

    3. 具有AddXXXHandler和RemoveXXXHandlers方法的委托变量

    选项1显然很可怕,因为我们厌恶公共变量的所有正常原因。

    选项2稍好一些,但允许订阅者有效地相互覆盖——编写someInstance太容易了。MyEvent=事件处理程序;这将替换任何现有的事件处理程序,而不是添加一个新的。此外,您仍然需要编写属性。

    选项3基本上就是事件给你的,但有一个有保证的约定(由编译器生成,并由IL中的额外标志支持)和一个“自由”的实现,如果你对类似字段的事件给你带来的语义感到满意的话。订阅和取消订阅事件是封装的,不允许任意访问事件处理程序列表,语言可以通过为声明和订阅提供语法来使事情变得更简单。

        5
  •  10
  •   Miguel Gamboa    10 年前

    活动和代表之间存在多么大的误解!!!委托指定一个TYPE(例如 class ,或 interface 确实如此),而事件只是一种成员(如字段、属性等)。而且,就像任何其他类型的成员一样,事件也有一种类型。然而,在事件的情况下,事件的类型必须由委托指定。例如,您不能声明由接口定义的类型的事件。

    最后,我们可以做以下几点 观察:事件的类型必须由代表定义 。这是活动和代表之间的主要关系,在本节中进行了描述 II、 18定义事件 属于的 ECMA-335 (CLI) Partitions I to VI :

    在典型使用中,TypeSpec(如果存在) 识别代表 其签名与传递给events-fire方法的参数相匹配。

    然而, 这一事实并不意味着事件使用了支持委托字段 事实上,一个事件可能会使用您选择的任何不同数据结构类型的支持字段。如果你在C#中显式地实现了一个事件,你可以自由选择存储事件的方式 事件处理程序 (注意 事件处理程序 是以下实例 事件类型 ,这反过来又是强制性的 委托类型 ---从以前 观察 ).但是,您可以将这些事件处理程序(即委托实例)存储在数据结构中,例如 List 或a Dictionary 或者甚至在支持委托字段中。但不要忘记,您不必使用委托字段。

        6
  •  8
  •   Community Mohan Dere    8 年前

    注意:如果您有权访问 C# 5.0 Unleashed ,请阅读第18章“事件”中的“简单使用代表的限制”,以更好地理解两者之间的差异。


    它总是帮助我有一个简单,具体的例子。这里有一个社区。首先,我将演示如何单独使用委托来完成Events为我们做的事情。然后,我将展示相同的解决方案如何与以下实例一起工作 EventHandler 然后我解释了为什么我们不想做我在第一个例子中解释的事情。这篇文章的灵感来自 an article 约翰·斯基特。

    示例1:使用公共委托

    假设我有一个带有单个下拉框的WinForms应用程序。下拉菜单绑定到 List<Person> 其中Person具有Id、Name、NickName、HairColor属性。主窗体上有一个自定义用户控件,显示该用户的属性。当有人在下拉列表中选择一个人时,用户控件中的标签会更新以显示所选人员的属性。

    enter image description here

    这是它的工作原理。我们有三个文件可以帮助我们将其组合在一起:

    • Mediator.cs——静态类保存委托
    • Form1.cs——主表格
    • DetailView.cs--用户控件显示所有详细信息

    以下是每个类的相关代码:

    class Mediator
    {
        public delegate void PersonChangedDelegate(Person p); //delegate type definition
        public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
        public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
        {
            if (PersonChangedDel != null)
            {
                PersonChangedDel(p);
            }
        }
    }
    

    以下是我们的用户控制:

    public partial class DetailView : UserControl
    {
        public DetailView()
        {
            InitializeComponent();
            Mediator.PersonChangedDel += DetailView_PersonChanged;
        }
    
        void DetailView_PersonChanged(Person p)
        {
            BindData(p);
        }
    
        public void BindData(Person p)
        {
            lblPersonHairColor.Text = p.HairColor;
            lblPersonId.Text = p.IdPerson.ToString();
            lblPersonName.Text = p.Name;
            lblPersonNickName.Text = p.NickName;
    
        }
    }
    

    最后,我们在Form1.cs中有以下代码。这里我们调用OnPersonChanged,它调用订阅给委托的任何代码。

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
        Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
    }
    

    好的。这就是你如何让这个工作 不使用事件 仅使用代理 我们只是将一个公共委托放入一个类中——你可以将其设置为静态或单例,或者其他任何形式。太棒了

    但是,但是,但是我们不想做我刚才描述的事情。因为 public fields are bad 原因有很多。那么,我们有什么选择呢?正如John Skeet所描述的,以下是我们的选择:

    1. 一个公共委托变量(这就是我们刚才做的。不要这样做。我刚才告诉过你为什么它不好)
    2. 将委托放入带有get/set的属性中(这里的问题是订阅者可能会相互覆盖——所以我们可以为委托订阅一堆方法,然后我们可能会意外地说 PersonChangedDel = null ,清除所有其他订阅。这里仍然存在的另一个问题是,由于用户可以访问委托,他们可以调用调用列表中的目标——我们不希望外部用户有权访问何时引发我们的事件。
    3. 具有AddXXXHandler和RemoveXXXHandlers方法的委托变量

    第三种选择本质上就是事件给我们的。当我们声明一个事件处理程序时,它让我们可以访问一个委托——不是公开的,也不是作为一个属性,但我们称之为一个只有添加/删除访问器的事件。

    让我们看看相同的程序是什么样子的,但现在使用事件而不是公共委托(我还将我们的中介更改为单例):

    示例2:使用事件处理程序而不是公共委托

    调解员:

    class Mediator
    {
    
        private static readonly Mediator _Instance = new Mediator();
    
        private Mediator() { }
    
        public static Mediator GetInstance()
        {
            return _Instance;
        }
    
        public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
    
        public void OnPersonChanged(object sender, Person p)
        {
            var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
            if (personChangedDelegate != null)
            {
                personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
            }
        }
    }
    

    请注意,如果您在EventHandler上按F12,它将显示定义只是一个带有额外“sender”对象的通用化委托:

    public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
    

    用户控制:

    public partial class DetailView : UserControl
    {
        public DetailView()
        {
            InitializeComponent();
            Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
        }
    
        void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
        {
            BindData(e.Person);
        }
    
        public void BindData(Person p)
        {
            lblPersonHairColor.Text = p.HairColor;
            lblPersonId.Text = p.IdPerson.ToString();
            lblPersonName.Text = p.Name;
            lblPersonNickName.Text = p.NickName;
    
        }
    }
    

    最后,这是Form1.cs代码:

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
            Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
    }
    

    因为事件处理程序希望将和ViewModel作为参数,所以我创建了一个只有一个属性的类:

    class PersonChangedEventArgs
    {
        public Person Person { get; set; }
    }
    

    希望这能让你了解一下为什么我们有活动,以及它们作为代表是如何不同的,但在功能上是相同的。

        7
  •  5
  •   Paul Hill    15 年前

    您还可以在接口声明中使用事件,但不适用于委托。

        8
  •  5
  •   Weidong Shen    7 年前

    Delegate是一个类型安全的函数指针。事件是使用委托实现发布者-订阅者设计模式。

        9
  •  4
  •   supercat    14 年前

    .net中的事件是Add方法和Remove方法的指定组合,这两种方法都需要某种特定类型的委托。C#和vb.net都可以自动生成add和remove方法的代码,这些方法将定义一个持有事件订阅的委托,并向该订阅委托添加/从该订阅委托中删除传入的委托。VB.net还将自动生成代码(使用RaiseEvent语句),以在订阅列表非空时调用订阅列表;出于某种原因,C#不会生成后者。

    请注意,虽然使用多播委托管理事件订阅很常见,但这并不是唯一的方法。从公共角度来看,潜在的事件订阅者需要知道如何让对象知道它想要接收事件,但它不需要知道发布者将使用什么机制来引发事件。还要注意的是,虽然在.net中定义事件数据结构的人显然认为应该有一种公开的方法来提高它们,但C#和vb.net都没有利用这一特性。

        10
  •  2
  •   Venkatesh Muniyandi    9 年前

    以简单的方式定义事件:

    事件是 参考 给有两个限制的代表

    1. 不能直接调用
    2. 不能直接赋值(例如eventObj=delegateMethod)

    以上两点是代表们的弱点,将在活动中加以解决。显示fiddler差异的完整代码示例在这里 https://dotnetfiddle.net/5iR3fB .

    在事件和委托以及调用/分配值给委托的客户端代码之间切换注释,以了解差异

    这是内联代码。

     /*
    This is working program in Visual Studio.  It is not running in fiddler because of infinite loop in code.
    This code demonstrates the difference between event and delegate
            Event is an delegate reference with two restrictions for increased protection
    
                1. Cannot be invoked directly
                2. Cannot assign value to delegate reference directly
    
    Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
    */
    
    public class RoomTemperatureController
    {
        private int _roomTemperature = 25;//Default/Starting room Temperature
        private bool _isAirConditionTurnedOn = false;//Default AC is Off
        private bool _isHeatTurnedOn = false;//Default Heat is Off
        private bool _tempSimulator = false;
        public  delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
        // public  OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 
        public  event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 
    
        public RoomTemperatureController()
        {
            WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
        }
        private void InternalRoomTemperatuerHandler(int roomTemp)
        {
            System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
        }
    
        //User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
        public bool TurnRoomTeperatureSimulator
        {
            set
            {
                _tempSimulator = value;
                if (value)
                {
                    SimulateRoomTemperature(); //Turn on Simulator              
                }
            }
            get { return _tempSimulator; }
        }
        public void TurnAirCondition(bool val)
        {
            _isAirConditionTurnedOn = val;
            _isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
            System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
            System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
    
        }
        public void TurnHeat(bool val)
        {
            _isHeatTurnedOn = val;
            _isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
            System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
            System.Console.WriteLine("Heat :" + _isHeatTurnedOn);
    
        }
    
        public async void SimulateRoomTemperature()
        {
            while (_tempSimulator)
            {
                if (_isAirConditionTurnedOn)
                    _roomTemperature--;//Decrease Room Temperature if AC is turned On
                if (_isHeatTurnedOn)
                    _roomTemperature++;//Decrease Room Temperature if AC is turned On
                System.Console.WriteLine("Temperature :" + _roomTemperature);
                if (WhenRoomTemperatureChange != null)
                    WhenRoomTemperatureChange(_roomTemperature);
                System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
            }
        }
    
    }
    
    public class MySweetHome
    {
        RoomTemperatureController roomController = null;
        public MySweetHome()
        {
            roomController = new RoomTemperatureController();
            roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
            //roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
            //roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
            roomController.SimulateRoomTemperature();
            System.Threading.Thread.Sleep(5000);
            roomController.TurnAirCondition (true);
            roomController.TurnRoomTeperatureSimulator = true;
    
        }
        public void TurnHeatOrACBasedOnTemp(int temp)
        {
            if (temp >= 30)
                roomController.TurnAirCondition(true);
            if (temp <= 15)
                roomController.TurnHeat(true);
    
        }
        public static void Main(string []args)
        {
            MySweetHome home = new MySweetHome();
        }
    
    
    }
    
        11
  •  2
  •   NeoZoom.lua    5 年前

    因为人们生活在2020年,想要一个干净的答案。..

    定义:

    • delegate :定义函数指针。
    • event :定义
      • 1. 受保护的 接口,
      • (2) 操作( += , -= ),
      • (3) 优点:你不需要使用 new 关键字了。

    关于形容词 受保护的 :

    // eventTest.SomeoneSay = null;              // Compile Error.
    // eventTest.SomeoneSay = new Say(SayHello); // Compile Error.
    

    另请注意Microsoft的此部分: https://learn.microsoft.com/en-us/dotnet/standard/events/#raising-multiple-events

    代码示例:

    具有 代表 :

    public class DelegateTest
    {
        public delegate void Say(); // Define a pointer type "void <- ()" named "Say".
        private Say say;
    
        public DelegateTest() {
            say  = new Say(SayHello);     // Setup the field, Say say, first.
            say += new Say(SayGoodBye);
            
            say.Invoke();
        }
        
        public void SayHello() { /* display "Hello World!" to your GUI. */ }
        public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
    }
    

    具有 事件 :

    public class EventTest
    {
        public delegate void Say();
        public event Say SomeoneSay;  // Use the type "Say" to define event, an 
                                      // auto-setup-everything-good field for you.
        public EventTest() {
             SomeoneSay += SayHello;
             SomeoneSay += SayGoodBye;
    
             SomeoneSay();
        }
        
        public void SayHello() { /* display "Hello World!" to your GUI. */ }
        public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
    }
    

    参考:

    活动与代表 - 解释C#中事件和委托模式之间的重要区别以及它们为什么有用。 : https://dzone.com/articles/event-vs-delegate