请教一个C#定义Event的问题
请问publiceventbehindClickEventHandlerbehindClick;和publiceventbehindClickEventHandlerbe...
请问
public event behindClickEventHandler behindClick;
和
public event behindClickEventHandler behindClick
{
add
{
Events.AddHandler(MyObj, value);
}
remove
{
Events.RemoveHandler(MyObj, value);
}
}
二者的区别是什么?后面的那个什么意思?
谢谢大家的回答。还想问一下,为什么使用后面那种方法时。
behindClick!=null时会报错。它是否定义了behindClick事件,还是只是在定义add和remove方法? 展开
public event behindClickEventHandler behindClick;
和
public event behindClickEventHandler behindClick
{
add
{
Events.AddHandler(MyObj, value);
}
remove
{
Events.RemoveHandler(MyObj, value);
}
}
二者的区别是什么?后面的那个什么意思?
谢谢大家的回答。还想问一下,为什么使用后面那种方法时。
behindClick!=null时会报错。它是否定义了behindClick事件,还是只是在定义add和remove方法? 展开
展开全部
前一个是定义了一个behindClickEventHandler,这样做的话默认实现了下面的add,remove之类代码,即默认的添加,删除事件处理器逻辑。
后面一个是定义了一个behindClickEventHandler并且定义了添加删除事件处理器的代码。
如果向第二个那样写的话,可以简写成第一个的形式,而不需要手动写add,remove。不过,当你需要在添加或删除事件的时候做一些其他的操作(比如记录日志)就需要手动写add和remove了。以下的例子是在添加,删除事件处理器的时候记录日志的代码:
public event behindClickEventHandler behindClick
{
add
{
Console.WriteLine("ADD EVENT");
Events.AddHandler(MyObj, value);
}
remove
{
Console.WriteLine("REMOVE EVENT");
Events.RemoveHandler(MyObj, value);
}
}
Ps: 这里只是定义了添加和删除eventhandler的方法而没有初始化behindClick本身。你可以简单的用property的定义来类比event的定义。
后面一个是定义了一个behindClickEventHandler并且定义了添加删除事件处理器的代码。
如果向第二个那样写的话,可以简写成第一个的形式,而不需要手动写add,remove。不过,当你需要在添加或删除事件的时候做一些其他的操作(比如记录日志)就需要手动写add和remove了。以下的例子是在添加,删除事件处理器的时候记录日志的代码:
public event behindClickEventHandler behindClick
{
add
{
Console.WriteLine("ADD EVENT");
Events.AddHandler(MyObj, value);
}
remove
{
Console.WriteLine("REMOVE EVENT");
Events.RemoveHandler(MyObj, value);
}
}
Ps: 这里只是定义了添加和删除eventhandler的方法而没有初始化behindClick本身。你可以简单的用property的定义来类比event的定义。
展开全部
C#编译器为事件的add和remove方法增加[MethodImpl (MethodImplOptions.Synchronized)]属性。这个属性的目的是为了确保在操作实例的事件成员时,对于任何一个对象,在同一时刻只能有一个add方法或者remove方法可以执行。该属性同样确保在操作静态事件成员时,同一时刻也只能有一个add方法或者remove方法可以执行。这里需要线程同步,以免委托对象的链表被破坏。但应注意,CLR实现线程同步的方式中存在许多问题。
对实例(非静态)方法应用MethodImpl属性时,CLR使用对象本身作为线程同步锁。这意味着如果类定义了许多事件,那么所有的add和remove方法都将使用相同的锁,这种情况下,如果有多个线程同时订阅和注销不同的事件,则会损害可扩展性。但这种情况非常少见,而且对于大多数应用程序,这并不是一个问题。但是,线程同步的指导方针中规定方法不应在对象本身上加同步锁,因为同步锁将对所有的代码公开。这意味着任何人都可以编写代码锁住这个对象,从而可能导致其他线程死锁。如果希望自己编写的类型防御措施好,更加健壮,那么应使用不同的对象来完成加锁功能。10.5节将示范如何实现这个 功能。
在静态方法上应用[MethodImpl(MethodImplOptions.Synchronized)]属性时,CLR使用类型对象作为线程同步锁。这又意味着如果类定义了许多静态事件,那么所有的add和remove方法都将使用相同的锁,这种情况下,如果有多个线程同时订阅和注销不同的事件,则会损害代码的性能。但是这种情况也非常少见。
但还有一个严重的问题:线程同步指导方针指出,方法永远不要在类型对象上加锁,因为这个锁将对所有的代码公开。另外,当类型加载与域无关时,CLR中还有一个错误。这种情况会导致同步锁被使用该类型的所有应用程序域共享,如此一来,一个程序域中的代码会影响另一个应用程序域中运行的代码。实际中,C#编译器在实现静态add和remove方法的线程安全时应采用完全不同的方法。10.5节将讨论一种修正C#编译器这一缺陷的 机制。
C#和CLR允许定义一个有一个或者多个实例(非静态)事件成员的值类型(结构)。但必须意识到,在上述情况下,C#编译器根本不会保证线程安全。因为拆箱(unboxing)的值类型没有与其相关联的锁对象。实际上,C#编译器不为add和remove方法生成[MethodImpl (MethodImplOptions.Synchronized)]属性,因为该属性对值类型的实例方法没有效果。遗憾的是,当实例事件定义为值类型的成员时,的确没有更好的方式为它们保证线程安全。因此,建议大家尽量避免这样做。注意,在值类型中定义的静态事件(加上前面讨论的限制)可以保证线程安全,因为静态事件对类型对象(引用类型)本身加锁。但是,如果要使代码健壮,应采用10.5节讨论的机制。
有时我们会感到编译器生成的add和remove方法不是那么理想。例如10.4节中讨论的使用MicrosoftC#编译器时遇到的所有线程安全问题。实际上,Microsoft的C#编译器在安全编程(defensive coding)和健壮性方面永远不是最安全的。为了创建一个坚固的组件,建议经常采用本节介绍的技术,该技术可以用于解决与线程安全相关的所有问题。而且该技术同样也可以应用于其他目的。例如,显式实现add和remove方法的普遍原因就是类型定义了许多事件,而且又需要高效地进行存储。有关详情请参见10.6节。
幸亏C#编译器以及其他许多编译器都允许开发人员显式地实现add和remove访问器方法。为了保证MailManager对象上事件的订阅和注销的线程安全,我们修改了MailManager类的代码,修改后的代码如下所示:
internal class MailManager {
//创建一个作为线程同步锁的私有实例字段
private readonly Object m_eventLock = new Object();
//增加一个引用委托链表头部的私有字段
private EventHandler<NewMailEventArgs> m_NewMail;
//为类增加一个事件成员
public event EventHandler<NewMailEventArgs> NewMail {
//显式实现'add'方法
add {
//加私有锁,并向委托链表增加一个处理程序(以'value'为参数)
lock (m_eventLock) { m_NewMail += value; }
}
//显式实现'remove'方法
remove {
//加私有锁,并从委托链表从中移除处理程序(以'value'为参数)
lock (m_eventLock) { m_NewMail -= value; }
}
}
对实例(非静态)方法应用MethodImpl属性时,CLR使用对象本身作为线程同步锁。这意味着如果类定义了许多事件,那么所有的add和remove方法都将使用相同的锁,这种情况下,如果有多个线程同时订阅和注销不同的事件,则会损害可扩展性。但这种情况非常少见,而且对于大多数应用程序,这并不是一个问题。但是,线程同步的指导方针中规定方法不应在对象本身上加同步锁,因为同步锁将对所有的代码公开。这意味着任何人都可以编写代码锁住这个对象,从而可能导致其他线程死锁。如果希望自己编写的类型防御措施好,更加健壮,那么应使用不同的对象来完成加锁功能。10.5节将示范如何实现这个 功能。
在静态方法上应用[MethodImpl(MethodImplOptions.Synchronized)]属性时,CLR使用类型对象作为线程同步锁。这又意味着如果类定义了许多静态事件,那么所有的add和remove方法都将使用相同的锁,这种情况下,如果有多个线程同时订阅和注销不同的事件,则会损害代码的性能。但是这种情况也非常少见。
但还有一个严重的问题:线程同步指导方针指出,方法永远不要在类型对象上加锁,因为这个锁将对所有的代码公开。另外,当类型加载与域无关时,CLR中还有一个错误。这种情况会导致同步锁被使用该类型的所有应用程序域共享,如此一来,一个程序域中的代码会影响另一个应用程序域中运行的代码。实际中,C#编译器在实现静态add和remove方法的线程安全时应采用完全不同的方法。10.5节将讨论一种修正C#编译器这一缺陷的 机制。
C#和CLR允许定义一个有一个或者多个实例(非静态)事件成员的值类型(结构)。但必须意识到,在上述情况下,C#编译器根本不会保证线程安全。因为拆箱(unboxing)的值类型没有与其相关联的锁对象。实际上,C#编译器不为add和remove方法生成[MethodImpl (MethodImplOptions.Synchronized)]属性,因为该属性对值类型的实例方法没有效果。遗憾的是,当实例事件定义为值类型的成员时,的确没有更好的方式为它们保证线程安全。因此,建议大家尽量避免这样做。注意,在值类型中定义的静态事件(加上前面讨论的限制)可以保证线程安全,因为静态事件对类型对象(引用类型)本身加锁。但是,如果要使代码健壮,应采用10.5节讨论的机制。
有时我们会感到编译器生成的add和remove方法不是那么理想。例如10.4节中讨论的使用MicrosoftC#编译器时遇到的所有线程安全问题。实际上,Microsoft的C#编译器在安全编程(defensive coding)和健壮性方面永远不是最安全的。为了创建一个坚固的组件,建议经常采用本节介绍的技术,该技术可以用于解决与线程安全相关的所有问题。而且该技术同样也可以应用于其他目的。例如,显式实现add和remove方法的普遍原因就是类型定义了许多事件,而且又需要高效地进行存储。有关详情请参见10.6节。
幸亏C#编译器以及其他许多编译器都允许开发人员显式地实现add和remove访问器方法。为了保证MailManager对象上事件的订阅和注销的线程安全,我们修改了MailManager类的代码,修改后的代码如下所示:
internal class MailManager {
//创建一个作为线程同步锁的私有实例字段
private readonly Object m_eventLock = new Object();
//增加一个引用委托链表头部的私有字段
private EventHandler<NewMailEventArgs> m_NewMail;
//为类增加一个事件成员
public event EventHandler<NewMailEventArgs> NewMail {
//显式实现'add'方法
add {
//加私有锁,并向委托链表增加一个处理程序(以'value'为参数)
lock (m_eventLock) { m_NewMail += value; }
}
//显式实现'remove'方法
remove {
//加私有锁,并从委托链表从中移除处理程序(以'value'为参数)
lock (m_eventLock) { m_NewMail -= value; }
}
}
参考资料: C#内幕
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询