当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《代表和活动之间有什么区别?》经验,为你挑选了7个好方法。

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



1> mmcdole..:

一个事件的声明增加了抽象和保护上一层委托实例.此保护可防止委托的客户端重置委托及其调用列表,并仅允许从调用列表中添加或删除目标.


如果当然,这个保护层还会阻止"客户端"(定义类/结构之外的代码)从_invoking_委托,以及以任何方式获取事件后面的委托对象.
不完全正确.您可以声明没有后端委托实例的事件.在c#中,您可以显式实现事件并使用您选择的不同后端数据结构.
@mmcdole您能提供一个例子来解释他吗?

2> faby..:

要了解这些差异,您可以看看这2个示例

代表示例(在本例中为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班级的班级都可以提高他们的RaiseEvent召唤力animal.RaiseEvent().

为了避免这些弱点,你可以events在c#中使用.

您的Animal类将以这种方式更改:

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

区别:

    您没有使用公共属性,而是使用公共字段(使用事件,编译器保护您的字段免受不必要的访问)

    无法直接分配事件.在这种情况下,它不会引起我在覆盖行为时显示的先前错误.

    班上没有人可以提出这个活动.

    事件可以包含在接口声明中,而字段则不能

笔记:

EventHandler被声明为以下委托:

public delegate void EventHandler (object sender, EventArgs e)

它需要一个发送者(对象类型)和事件参数.如果发件人来自静态方法,则它为null.

这个使用的例子EventHandler也可以用来EventHandler代替.

有关EventHandler的文档,请参阅此处


@Sung活动只能从课堂上升起,也许我一直不清楚解释这一点.使用事件,您可以调用引发事件的函数(封装),但它只能从定义它的类中升级.如果我不清楚,请告诉我.
一切都看起来很棒,直到我遇到"你班上没有人能举起这个活动." 那是什么意思?只要调用方法可以访问使用事件的代码中的`animal`实例,就不能有人调用`RaiseEvent`吗?

3> Jorge Córdob..:

除了语法和操作属性之外,还存在语义差异.

从概念上讲,代表是功能模板; 也就是说,它们表达了一个函数必须遵守的契约,以便被认为是代表的"类型".

事件代表......好吧,事件.它们旨在在某些事情发生时提醒某人,是的,它们遵循代理定义,但它们不是一回事.

即使它们完全相同(语法和IL代码),仍然会保留语义差异.一般来说,我更喜欢为两个不同的概念设置两个不同的名称,即使它们以相同的方式实现(这并不意味着我喜欢两次使用相同的代码).


代表们的优秀描述.

4> vibhu..:

这是另一个很好的链接. http://csharpindepth.com/Articles/Chapter2/Events.aspx

简而言之,从文章中删除 - 事件是对代表的封装.

文章引用:

假设事件在C#/ .NET中不作为概念存在.另一个班级如何订阅活动?三种选择:

    公共委托变量

    由属性支持的委托变量

    具有AddXXXHandler和RemoveXXXHandler方法的委托变量

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

选项2略胜一筹,但允许订阅者有效地互相覆盖 - 编写someInstance.MyEvent = eventHandler会很容易; 它将替换任何现有的事件处理程序,而不是添加新的事件处理程序.此外,您仍需要编写属性.

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



5> Trevor..:

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


有一个简单,具体的例子总能帮助我.所以这是社区的一个.首先,我将展示如何单独使用代表来完成事件为我们所做的事情.然后我展示了同一个解决方案如何与一个实例一起工作EventHandler.然后我解释为什么我们不想做我在第一个例子中解释的内容.这篇文章的灵感来自John Skeet 的一篇文章.

示例1:使用公共委托

假设我有一个带有单个下拉框的WinForms应用程序.下拉列表必然会出现问题List.Person具有Id,Name,NickName,HairColor的属性.在主窗体上是一个自定义用户控件,显示该人员的属性.当有人在下拉列表中选择某个人时,用户控件中的标签会更新以显示所选人员的属性.

在此输入图像描述

这是如何工作的.我们有三个文件可以帮助我们将它们放在一起:

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`.
}

好.这就是如何在不使用事件仅使用委托的情况下使其工作.我们只是将一个公共委托放入一个类 - 你可以使它成为静态或单个,或者其他什么.大.

但是,但是,我们不想做我刚才描述的事情.因为公共领域对许多人来说是坏事,很多原因.那么我们有什么选择呢?正如John Skeet所描述的,以下是我们的选择:

    一个公共委托变量(这是我们上面刚刚做过的.不要这样做.我刚才告诉你为什么它很糟糕)

    将委托放入带有get/set的属性中(这里的问题是订阅者可以互相覆盖 - 所以我们可以向委托订阅一堆方法,然后我们可能会意外地说PersonChangedDel = null,消除所有其他订阅.此处存在的另一个问题是,由于用户可以访问委托,因此他们可以调用调用列表中的目标 - 我们不希望外部用户有权访问何时提升我们的事件.

    具有AddXXXHandler和RemoveXXXHandler方法的委托变量

第三种选择基本上是一个事件给我们的东西.当我们声明一个EventHandler时,它允许我们访问一个委托 - 不是公开的,不是作为属性,但是我们称之为只添加/删除访问者的事件.

让我们看看相同的程序是什么样的,但现在使用Event代替公共委托(我还将我们的Mediator更改为单例):

示例2:使用EventHandler而不是公共委托

中保

class Mediator
{

    private static readonly Mediator _Instance = new Mediator();

    private Mediator() { }

    public static Mediator GetInstance()
    {
        return _Instance;
    }

    public event EventHandler 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;
        if (personChangedDelegate != null)
        {
            personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
        }
    }
}

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

public delegate void EventHandler(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);
}

因为EventHandler想要和EventArgs作为参数,所以我创建了这个类,其中只有一个属性:

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

希望这能告诉你为什么我们有事件以及它们如何不同 - 但在功能上与代表一样.



6> 小智..:

您还可以在接口声明中使用事件,而不是代理人使用事件.


@surfen接口可以包含事件,但不包含委托.

7> Miguel Gambo..:

事件和代表之间的误会真是太棒了!委托指定TYPE(例如a class或a interface),而事件只是一种成员(例如字段,属性等).并且,就像任何其他类型的成员一样,事件也有类型.但是,在事件的情况下,事件的类型必须由委托指定.例如,您不能声明由接口定义的类型的事件.

最后,我们可以进行以下观察:事件的类型必须由委托定义.这是一个事件和委托之间的主要关系,并在部分中描述II.18定义事件的ECMA-335(CLI)分区I至VI:

在典型用法中,TypeSpec(如果存在)标识一个委托,其签名与传递给事件的fire方法的参数匹配.

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

推荐阅读
贴进你的心聆听你的世界
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有