如果我有ClassA有一个公共事件,SomeEvent和ClassC有方法addListener接受一个EventHandler引用,为什么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 } }
解决方法
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.也不能知道“值”参数是具有设置器或普通字段的属性.
事件不太清楚,因为在工作中有很多语法糖.这个声明
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#编译器会自动生成add / remove访问器.它不为财产做这个.它会使自动属性更容易:
property int Property;
但是,这需要为该语言添加一个新的关键字,C#团队真的不喜欢.其他语言,如VB.NET和C/C++LI确实有这个关键字.