代码改变世界

【More Effective C#】仅声明非虚的事件

2010-10-13 08:17  空逸云  阅读(433)  评论(0编辑  收藏  举报

C#除了可以声明虚方法,属性外,也可以声明虚事件.但是并不建议使用虚事件.因为这常会带来不可预测的Bug.我们定义了一个WorkerEngine.它有一个虚事件.

 


public abstract class WorkerEngineBase
    {
        
public virtual event EventHandler<WorkerEventArgs> OnProgress;
        
public void DoLotsOfStuff()
        {
            OnProgress
+=new EventHandler<WorkerEventArgs>(WorkerEngineBase_OnProgress);
            WorkerEventArgs args 
= new WorkerEventArgs();
            args.Percent 
= 2;
            EventHandler
<WorkerEventArgs> progHandler = OnProgress;
            
if (progHandler != null)
            {
                progHandler(
this, args);
            }
            
if (args.Cancel)
                
return;
        }
        
void WorkerEngineBase_OnProgress(object sender, WorkerEventArgs e)
        {
            Console.WriteLine(
"WorkerEngineBase class event.");
        }
        
protected abstract void SomeWork();
    }

    
public class WorkerEventArgs : EventArgs
    {
        
public int Percent { getset; }
        
public bool Cancel { getset; }
    }

 

 C#3.0.编译器将会自动创建私有字段,以及公共addremove方法.

 


    Private EventHandler
<WorkerEventArgs>progressEvent;
    Public 
virtual event EventHandler<WorkerEventArgs>OnOrigress
    {
    Add
    {
    progressEvent
+=value;
    }
    Remove
    {
    progressEvent
-=value;
    }
    }

 

  由于该私有字段是编译器生成的.所以你无法通过代码访问.而且,派生事件将隐藏基类中的事件,派生类所作的工作与原始版本完全一致.即基类有一个自己的私有委托字段,派生类自己也有一个.两个互不相干.

 


public class WorkerEngineDerived : WorkerEngineBase
    {
        
protected override void SomeWork()
        {
            
//...do some thing;
        }

        
public override event EventHandler<WorkerEventArgs> OnProgress;
    }
    
    
//调用
WorkerEngineDerived worker = new WorkerEngineDerived();
worker.DoLotsOfStuff();

 

 结果并不与我们所想象的一致.

 

这里如前面所说.派生类访问的事件是派生类自己的事件.而非基类的事件..如果对派生类事件赋值.那么基类中的隐藏私有字段将不会得到赋值.其他类型订阅到的是派生类中的事件.解决的方案是基类使用类似字段方式声明的事件.

 


public class WorkerEngineDerived : WorkerEngineBase
    {
        
protected override void SomeWork()
        {
            
//...do some thing;
        }

        
public override event EventHandler<WorkerEventArgs> OnProgress
        {
            add
            {
                
base.OnProgress += value;
            }
            remove { 
base.OnProgress -= value; }
        }
    }

 

 子类以属性的方式重写.那么结果很显然便是我们所期望的

 

在创建虚事件时,不要使用字段方式的语法,而应该采用属性式的语法.

我们也可以创建一个虚方法,在定义虚事件后调用该方法触发,所有的派生类必须重写触发事件的方法以及虚事件.

 


public abstract class WorkerEngineBase
    {
        
public virtual event EventHandler<WorkerEventArgs> OnProgress;

        
protected virtual WorkerEventArgs RaiseEvent(WorkerEventArgs args)
        {
            EventHandler
<WorkerEventArgs> progHandler = OnProgress;
            
if (progHandler != null)
                progHandler(
this, args);
            
return args;
        }

        
public void DoLotsOfStuff()
        {
            OnProgress 
+= new EventHandler<WorkerEventArgs>(WorkerEngineBase_OnProgress);
            WorkerEventArgs args 
= new WorkerEventArgs();
            args.Percent 
= 2;
            RaiseEvent(args);
            
if (args.Cancel)
                
return;
        }

        
void WorkerEngineBase_OnProgress(object sender, WorkerEventArgs e)
        {
            Console.WriteLine(
"WorkerEngineBase class event.");
        }

        
protected abstract void SomeWork();
    }
    
 
public class WorkerEngineDerived : WorkerEngineBase
    {
        
protected override void SomeWork()
        {
            
//...do some thing;
        }

        
public override event EventHandler<WorkerEventArgs> OnProgress;

        
protected override WorkerEventArgs RaiseEvent(WorkerEventArgs args)
        {
            EventHandler
<WorkerEventArgs> proghandler = OnProgress;
            
if (proghandler != null)
                proghandler(
this, args);
            
return args;
        }
    }