在C#中为什么我不能传递另一个类'EventHandler引用,我该如何绕过它?

时间:2010-01-03 09:02:52

标签: c# events event-handling

如果我的ClassA具有公共事件,SomeEvent和ClassC,它具有接受EventHandler引用的方法addListener,为什么ClassB不能有一行显示c.addListener(ref a.SomeEvent)?如果我尝试,我会收到一个编译错误:“事件'ClassA.SomeEvent'只能出现在+ =或 - =的左侧(除非在'ClassA'类型中使用)。

为什么存在这种限制?如何在保持合理接近我的结构的同时解决它?

我是C#新手;任何帮助,将不胜感激。谢谢!

class ClassA {
    public event EventHandler SomeEvent;
}

ClassB{
    public ClassB() {

        ClassA a = new ClassA();
        ClassC c = new ClassC();
        c.addListener(ref a.SomeEvent);  //Compile error
    }
}

class ClassC {
    public void addListener(ref EventHandler handler) {
        handler += onEvent;
    }

    private void onEvent(object sender, EventArgs e) {
        //do stuff
    }

}

3 个答案:

答案 0 :(得分:9)

在课堂之外,您可以访问addremove访问者 - 这是您可以参加的事件的既不其他订阅者,也不改变它们(例如,将事件设置为null)。最好是正常处理事件,并导致你需要的任何后果。

想象一下,你可以做你的建议。例如,假设您订阅了一个按钮单击,并且某些其他代码使用该信息将您挂钩到“tick”事件 - 您的代码将无法按预期运行=错误。

要明白这一点;一个事件一个EventHandler,就像一个属性不是一个int一样 - 事件/属性定义存取方法

重新设置您的方案,将OnEvent公开并使用a.SomeEvent += c.OnEvent;,或者使用某些类似方法并使用匿名方法:

a.SomeEvent += delegate { c.DoSomethingCool(); };

答案 1 :(得分:6)

event关键字为私有委托对象创建一个访问者。属性完全相同,它限制对私有字段的访问。当您使用属性而不是事件时,您的代码段失败并出现类似的错误:

  class ClassA {
    public int Property { get; set; }
  }
  class ClassB {
    public ClassB() {
      ClassA a = new ClassA();
      ClassC c = new ClassC();
      c.setValue(ref a.Property);   // CS0206
    }
  }
  class ClassC {
    public void setValue(ref int value) {
      value = 42;
    }
  }

现在更容易看到,编译器无法确保setValue()方法使用属性setter。也不知道“值”参数是具有setter或plain字段的属性。

事件不太清楚,因为工作中有太多的语法糖。这个声明

public event EventHandler SomeEvent;

实际上生成了这段代码:

private EventHandler _SomeEvent;
public event SomeEvent {
  add    { _SomeEvent += new EventHandler(value); }
  remove { _SomeEvent -= new EventHandler(value); }
}

添加和删除访问器等同于属性的get和set访问器,它们可以防止代码弄乱私有_SomeEvent字段。按照惯例,当您使用+ =时调用add访问器,使用 - =调用remove。将此与我给出的属性的早期示例进行比较。同样的问题,你不能使用ref关键字和ClassC.addListener()无法知道处理程序实际上是一个事件而不是委托对象。如果编译器会传递_SomeEvent,那么使用访问器的点就会丢失。

您可以重新构建代码以解决此问题:

  class ClassC {
    public EventHandler getListener() {
      return new EventHandler(onEvent);
    }
    private void onEvent(object sender, EventArgs e) { }
  }
...
      a.SomeEvent += c.getListener();

最后要注意的是:事件和属性之间的对称性有点丢失,如果您没有明确地编写它们,C#编译器会自动生成添加/删除访问器。它不会对财产这样做。它会使自动属性变得更容易:

property int Property;

但是这需要在语言中添加一个新的关键字,这是C#团队真正不喜欢的。其他语言如VB.NET和C ++ / CLI都有这个关键字。

答案 2 :(得分:0)

  

如何在合理地靠近我的结构的同时绕过它?

改为使用a.SomeEvent += handler

  

为什么存在这种限制?

见Marc Gravell的回答。