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

New*_*ang 10 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
    }

}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 9

在类之外,您只能访问addremove访问者 - 这是您既不能看到其他订阅者也不能更改它们的事件(例如,将事件设置为null).最好正常处理事件,并导致您需要的任何后果.

想象一下,你可以做你的建议.例如,假设您订阅了一个按钮单击,而其他一些代码使用该信息将您挂钩到"tick"事件 - 您的代码不会像预期的那样工作= bug.

为了表明这一点; 的事件是不是一个EventHandler,以同样的方式,一个属性是不是一个int-事件/属性定义的存取方法.

重新您的方案,要么使OnEvent公众和使用a.SomeEvent += c.OnEvent;,或者有一些类似的方法,并使用匿名法:

a.SomeEvent += delegate { c.DoSomethingCool(); };
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

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

事件不太清楚,因为工作中有太多的语法糖.这个宣言

public event EventHandler SomeEvent;
Run Code Online (Sandbox Code Playgroud)

实际生成此代码:

private EventHandler _SomeEvent;
public event SomeEvent {
  add    { _SomeEvent += new EventHandler(value); }
  remove { _SomeEvent -= new EventHandler(value); }
}
Run Code Online (Sandbox Code Playgroud)

添加和删​​除访问器等同于属性的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();
Run Code Online (Sandbox Code Playgroud)

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

property int Property;
Run Code Online (Sandbox Code Playgroud)

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