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)
在类之外,您只能访问add
和remove
访问者 - 这是您既不能看到其他订阅者也不能更改它们的事件点(例如,将事件设置为null).最好正常处理事件,并导致您需要的任何后果.
想象一下,你可以做你的建议.例如,假设您订阅了一个按钮单击,而其他一些代码使用该信息将您挂钩到"tick"事件 - 您的代码不会像预期的那样工作= bug.
为了表明这一点; 的事件是不是一个EventHandler
,以同样的方式,一个属性是不是一个int
-事件/属性定义的存取方法.
重新您的方案,要么使OnEvent
公众和使用a.SomeEvent += c.OnEvent;
,或者有一些类似的方法,并使用匿名法:
a.SomeEvent += delegate { c.DoSomethingCool(); };
Run Code Online (Sandbox Code Playgroud)
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都有这个关键字.