如果我的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
}
}
答案 0 :(得分:9)
在课堂之外,您仅可以访问add
和remove
访问者 - 这是您可以参加的事件的点既不看其他订阅者,也不改变它们(例如,将事件设置为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的回答。