Phoenix's Blog

博客园 首页 新随笔 联系 订阅 管理
  25 Posts :: 3 Stories :: 25 Comments :: 0 Trackbacks
同事有个这样的需求:需要判断一个对象的event是否已经被注册过了,如果没有,则注册一个事件来执行默认操作。
比方说类ClassA
    public class ClassA
    
{
        
public event EventHandler Event1;

        
public EventHandler Delegate1;
        
private string id;

        
protected virtual void OnEvent()
        
{
            
if (this.Event1 != null)
            
{
                
this.Event1(this, EventArgs.Empty);
            }

        }


        
public ClassA()
        
{
            
this.Delegate1 = delegate(object sender, EventArgs e)
            
{
                Console.WriteLine(
"Delegate");
            }
;
        }


        
public void Test()
        
{
            OnEvent();
        }

    }

现在在代码中已经得到一个ClassA的实例,但是如果得到其Event1的情况呢,我们首先想到了使用Reflection, 查到在Reflection中有个EventInfo类型
ClassA a = new ClassA();

EventInfo ei 
= typeof(ClassA).GetEvent("Event1");

可是这个EventInfo只能通过
void AddEventHandler(object target, Delegate handler);
void RemoveEventHandler(object target, Delegate handler);
这两个方法对已知的target进行事件的Hook up (http://msdn2.microsoft.com/zh-cn/library/ms228976.aspx),无法使用如PropertyInfo, FieldInfo之类的GetValue方法得到一个MulticastDelegate实例。

后来想想,事件的声明和公有Field的声明一样,唯独多了一个event关键字,但是采用这种方式还是不能得到一个FieldInfo
FieldInfo fi = typeof(ClassA).GetField("Event1", BindingFlags.Public | BindingFlags.Instance);

不甘心之余用ildasm查看了一下,终于发现一个简单的event声明,编译器实际上处理了如下操作:
        private event EventHandler Event1;

    
        
public void add_Event1(EventHandler value)
        
{
            
// Do addition.
        }


        
public void remove_Event1(EventHandler value)
        
{
            
// Do removal.
        }

所以实际上我们必须是得到一个私有的Field Event1,于是将代码改了改
FieldInfo fi = typeof(ClassA).GetField("Event1", BindingFlags.NonPublic | BindingFlags.Instance);
呵呵,终于能够得到一个FieldInfo的实例了,接下来使用FieldInfo.GetValue方法得到一个MulticastDelegate (未注册事件时为null). 这样我们可以对这个MulticastDelegate对象进行操作,实现真正的事件Hook up.

奇怪的是类似的代码在同事那边执行不过,用反编译工具查看了一下,发现其事件是采用自定义的方式

        public event EventHandler Event2
        
{
            add
            
{

            }


            remove
            
{

            }

        }

真正的事件delegate是存放在一个EventHandlerList中 (请参阅MSDN: .Net Framework Developer's Guide: Handle multiple events using event properties.)
所以接下来的就是找到这个EventHandlerList和MulticastDelegate的key,然后取得这个MulticastDelegate.

使用EventHandlerList易于管理,例如在Dispose资源时,统一Fire事件时等等,但是EventHandlerList本身是线性列表的对象,当这个List里面的delegate变得非常多时,查询的效率会变得非常差. :)
posted on 2007-12-26 15:01 Phoenix 阅读(1491) 评论(9)  编辑 收藏 网摘 所属分类: .Net

评论

#1楼  2007-12-26 15:10 A.Z! [未注册用户]
当这个List里面的delegate变得非常多时,查询的效率会变得非常差. :)

一个对象event再多也不会超过100个
  回复  引用    

#2楼  2007-12-26 15:12 留恋星空      
有什么作用?
  回复  引用  查看    

#3楼  2007-12-26 16:25 overred      
Activator.CreateInstance
  回复  引用  查看    

#4楼  2007-12-26 17:35 装配脑袋      
add和remove是Event唯一暴露的功能,其他做法都是不可靠的,在特定对象模型上的行为无法预测
  回复  引用  查看    

#5楼  2007-12-26 18:33 omnislash      
这种情况用回调接口更合适一些
  回复  引用  查看    

#6楼  2007-12-26 18:34 jillzhang      
你这样用事件,出发点是错误的,事件的目的是让类的外部能参与类的动作,而且不分外部的环境
你这样,我觉得用接口比较好
  回复  引用  查看    

#7楼 [楼主] 2007-12-26 18:48 Phoenix      
@jillzhang
@留恋星空
@omnislash

呵呵,特定情况当然得特定处理,我们这边的scenario是这样的:大量的输入Form是比较类似的,所以我们为BO做了元数据表,根据BO的元数据来修饰UI控件(BO的展示),比方说长度,帮助信息,校验,格式等等。现在我们修饰一个第三方的Grid控件,其中某一个功能是当Grid中的数据改变掉了,需要进行Validatation,所以在修饰类中是通过Grid的ValidateRow事件来处理的,但问题是,在特殊场景时,这个Grid可以让开发人员手动编写Validatation的处理。所以就出现了以上这种处理。
  回复  引用  查看    

说实话,看了半天发现楼主还是没有解决开头提出的问题。另外我个人觉得事件的默认值不可能是null的。
  回复  引用    

#9楼 [楼主] 2007-12-27 20:19 Phoenix      
@新手啊,这名字都有人抢
对于不知道的事情请不要用“觉得”,否则就不敢去尝试,不敢去思考。
  回复  引用  查看    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-12-26 15:04 编辑过
Google站内搜索


China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
近千种 9-95 新二手计算图书火热销售中!

相关文章:

相关链接: