我不明白纯委托字段和事件字段之间的区别

本文关键字:字段 事件 区别 之间 明白 | 更新日期: 2023-09-27 18:35:10

代表:我明白。但是当我转到活动时,很多事情我不太了解。我读了书,MSDN和网络上的一些简单示例,它们都具有相同的结构。例如,这里是链接:事件示例

我举第一个例子,作者说这是关于 C# 事件最简单的例子。

这是他的代码:

public class Metronome
{
    public event TickHandler Tick;
    public EventArgs e = null;
    public delegate void TickHandler(Metronome m, EventArgs e);
    public void Start()
    {
        while (true)
        {
            System.Threading.Thread.Sleep(3000);
            if (Tick != null)
            {
                Tick(this, e);
            }
        }
    }
}
public class Listener
{
    public void Subscribe(Metronome m)
    {
        m.Tick += new Metronome.TickHandler(HeardIt);
    }
    private void HeardIt(Metronome m, EventArgs e)
    {
        System.Console.WriteLine("HEARD IT");
    }
}
class Test
{
    static void Main()
    {
        Metronome m = new Metronome();
        Listener l = new Listener();
        l.Subscribe(m);
        m.Start();
    }
}

您可以注意到行:public event TickHandler Tick。当我更改为public TickHandler Tick时,程序仍然运行相同。但是新行我理解,因为它只是一个纯粹的代表。

所以,我的问题是:event行关键字的真正目的是什么:public event TickHandler Tick。这非常重要,因为所有的例子总是这样用,但我无法解释为什么。

谢谢:)

我不明白纯委托字段和事件字段之间的区别

委托和事件是相关的概念,但它们不是一回事。术语"代表"往往有两个含义(经常被掩盖):

  • 类似于单个方法接口的委托类型。(存在显着差异,但这是一个合理的起点。
  • 该类型的实例,通常通过方法组创建,以便在"调用"委托时调用该方法。

事件不是这些。它是一种类型的成员 - 一对添加/删除方法,接受委托订阅或取消订阅事件。使用 foo.SomeEvent += handler;foo.SomeEvent -= handler; 时,将使用 add 和 remove 方法。

这与属性实际上是一对 get/set 方法(或者可能只是两者之一)的方式非常相似。

当您声明类似字段的事件时,如下所示:

public event TickHandler Tick;

编译器将成员添加到您的类中,这些成员有点像这样:

private TickHandler tick;
public event TickHandler
{
    add { tick += value; }
    remove { tick -= value; }
}

它比这复杂一些,但这是基本思想 - 它是事件的简单实现,就像自动实现的属性一样。从类内部,您可以访问支持字段,而在类外部,您最终总是只使用该事件。

我个人认为很遗憾,类字段事件的声明看起来很委托类型的字段 - 它会导致在某些答案中发现一些误导性 (IMO) 语句,就好像 event 关键字"修改"字段声明一样 - 而实际上这意味着您正在声明完全不同的内容。我认为如果类似字段的事件看起来更像是自动实现的属性,例如

// Not real C#, but I wish it were...
public event TickHandler Tick { add; remove; }

我有一整篇文章详细介绍了,您可能会发现它很有用。

> event关键字基本上限制了对delegate的操作。您不能再使用 = 运算符手动分配它。

您只能从活动中逐个添加(使用 += )或删除(使用 -= )代表。这样做是为了防止某些订阅者"覆盖"其他订阅。

因此,您不能执行以下操作: m.Tick = new Metronome.TickHandler(HeardIt)

" event " 是一个修饰符。有什么好处?

  1. 您可以在接口中使用事件
  2. 只有声明它的类才能调用事件
  3. 事件公开一个addremove访问器,您可以重写该访问器并执行自定义操作
  4. 事件将您限制为分配方法的特定签名SomeMethod(object source, EventArgs args)该方法为您提供有关事件的其他信息。

你是对的 - 添加 event 关键字似乎几乎是多余的。但是,作为事件的字段和键入为纯委托的字段之间存在关键区别。使用 event 关键字意味着包含对象外部的对象可以订阅委托,但不能调用委托。删除事件关键字时,外部对象可以订阅并调用委托(可见性允许)。

将侦听器添加到程序时,添加的是事件,而不是委托

查看您的代码 m.勾选 +=

您看到该部分是您请求属性(类型事件),并且正在向其添加具有+=的侦听器。现在,您只能向该 Tick 属性添加 TickHandler 类型,如果您覆盖它,则必须创建自己的与 TickHandler 相同的格式。

就像添加到字符串或整数时一样。

string stringTest = string.Empty;
stringTest += "this works";
stringTest += 4; //this doesn't though
int intTest = 0;
intTest += 1; //works because the type is the same
intTest += "This doesn't work";
Metronome m = new Metronome();
Metronome.TickHandler myTicker = new Metronome.TickHandler(function);
m.Tick += myTicker; //works because it is the right type
m.Tick += 4; //doesn't work... wrong type
m.Tick += "This doesnt work either"; //string type is not TickHandler type

这能澄清一些吗?

据我所知,事件基本上是一个多播委托,但对基本操作有不同的访问规则,可以从定义它们的类内部或外部对委托和事件执行。

这些操作是:

使用 = 运算符进行分配

使用 += 和 -= 运算符添加/删除

使用 () 运算符调用

              Operation         | delegate   | event
              ------------------+------------+--------
Inside class  += / -=           | valid      | valid
              ------------------+------------+--------
Inside class  =                 | valid      | valid
              ------------------+------------+--------
Inside class  ()                | valid      | valid
              ------------------+------------+--------
Outside class  += / -=          | valid      | valid
              ------------------+------------+--------
Outside class  =                | valid      | not valid
              ------------------+------------+--------
Outside class  ()               | valid      | not valid

这为您提供了封装,这始终是良好的OOP风格。

我认为使用委托和事件之间的主要区别在于事件只能由服务器引发(意味着类的作者)

如果现在删除 event 关键字,则可以在Listener中提高m.Tick(sender,e),否则不会。

public class Listener
{
  public void Subscribe(Metronome m)
  {
    m.Tick += new Metronome.TickHandler(HeardIt);
  }
  private void RaisTick(object sender, EventArgs e)
  {
      m.Tick(sender,e);
  }
  private void HeardIt(Metronome m, EventArgs e)
  {
     System.Console.WriteLine("HEARD IT");
  }

}