5

代表:我明白。但是当我转向事件时,很多事情我不太了解。我在 Network 上阅读了书籍、MSDN 和一些简单的示例,它们都有相同的结构。例如,这里是链接:事件示例

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

这是他的代码:

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关键字 in line 的真正目的是什么:public event TickHandler Tick。这很重要,因为所有的例子总是这样使用,但我无法解释为什么。

谢谢 :)

4

7 回答 7

9

代表和事件是相关的概念,但它们不是一回事。“代表”一词往往有两种含义(经常被掩盖):

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

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

这与属性实际上是一对 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; }

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

于 2012-05-22T17:35:13.423 回答
7

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

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

因此,您不能这样做:m.Tick = new Metronome.TickHandler(HeardIt)

于 2012-05-22T17:01:18.847 回答
3

event”是修饰语。有什么好处?

  1. 您可以在接口中使用事件
  2. 只有声明它的类才能调用事件
  3. 事件公开一个addremove访问器,您可以覆盖并执行自定义操作
  4. 事件将您限制为指定方法的特定签名,该方法SomeMethod(object source, EventArgs args)为您提供有关事件的附加信息。
于 2012-05-22T17:08:29.890 回答
1

当您向程序添加侦听器时,您添加的是事件,而不是委托

查看您的代码 m.Tick +=

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

很像添加到字符串或 int 时。

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

这可以清除一些吗?

于 2012-05-22T17:07:17.930 回答
1

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

于 2012-05-22T17:02:54.110 回答
1

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

操作是:

使用 = 运算符赋值

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

使用 () 运算符调用

              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 风格的封装。:-)

于 2012-05-22T17:08:04.243 回答
0

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

如果您event现在删除关键字,您可以m.Tick(sender,e)Listener其他情况下提出。

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");
  }

}

于 2012-05-22T17:03:26.253 回答