2012-09-29 72 views
1

我正在试验一个对象可以用来激发自己事件的扩展方法。将参数隐式转换为委托的扩展方法?

我已经得到它的工作几乎我想如何我可以改善它的参数传递的点可以转换为EventArgs的构造函数参数,而不诉诸激活。

我会说的前期,我很怀疑这是可能的,但我想给它一个镜头,因为有时我在编码技巧等都有很惊讶......

void Main() 
{ 
    var c = new C(); 
    c.E += (s, e) => Console.WriteLine (e.Message); 
    c.Go(); 
} 

public class C 
{ 
    public event EventHandler<Args> E; 
    public void Go() 
    { 
     Console.WriteLine ("Calling event E..."); 

     // This version doesn't know the type of EventArgs so it has to use Activator 
     this.Fire(E, "hello"); 

     // This version doesn't know ahead of time if there are any subscribers so it has to use a delegate 
     this.Fire(E,() => new Args("world")); 

     // Is there some way to get the best of both where it knows the type but can delay the 
     // creation of the event args? 
     //this.Fire<Args>("hello"); 
    } 
} 

public class Args : EventArgs 
{ 
    public Args(string s) 
    { 
     Message = s; 
    } 
    public string Message { get; set; } 
} 

public static class Ext 
{ 
    public static void Fire<T>(this object source, EventHandler<T> eventHander, Func<T> eventArgs) where T : EventArgs 
    { 
     if (eventHander != null) 
      eventHander(source, eventArgs()); 
    } 

    public static void Fire<T>(this object source, EventHandler<T> eventHander, params object[] args) where T : EventArgs 
    { 
     if (eventHander != null) 
      eventHander(source, (T)Activator.CreateInstance(typeof(T), args)); 
    } 
} 

回答

2

我之前做过这样的事情,但我采取了使用新的EventArgs/EventHandler包装器的路径。它使用隐式转换和泛型来自动地处理事件参数的转换。

public delegate void DataEventHandler<TSender, TEventArgs>(TSender sender, DataEventArgs<TEventArgs> eventArgs); 
public delegate void DataEventHandler<TEventArgs>(DataEventArgs<TEventArgs> eventArgs); 

public class DataEventArgs<TEventArgs> 
{ 
    public TEventArgs Args { get; private set; } 

    public DataEventArgs(TEventArgs args) 
    { 
     this.Args = args; 
    } 

    public static implicit operator TEventArgs(DataEventArgs<TEventArgs> args) 
    { 
     return args.Args; 
    } 

    public static implicit operator DataEventArgs<TEventArgs>(TEventArgs args) 
    { 
     return new DataEventArgs<TEventArgs>(args); 
    } 
} 

我把重载放在/没有发件人,可能不是一个好主意,但你至少可以玩弄它。

然后扩展方法,而不是放置在object类型的还挺吮吸因为那样所有对象(我认为)已经显示出他们的智能感知/使用它,即使它不是真正适用的,我把它绑在DataEventHandlers自己:

public static class MyExtensions 
{ 
    public static void Fire<TSender, TEventArgs>(this DataEventHandler<TSender, TEventArgs> eventHandler, TSender sender, TEventArgs args) 
    { 
     if (eventHandler!= null) 
      eventHandler(sender, args); 
    } 

    public static void Fire<TEventArgs>(this DataEventHandler<TEventArgs> eventHandler, TEventArgs args) 
    { 
     if (eventHandler != null) 
      eventHandler(args); 
    } 
} 

(注意我把它在相同的命名空间DataEventHandler所以它还会自动/可与他们假设您使用自己的命名空间中的事件using语句进口)

的extensi on方法已经知道参数类型,但它不会作为args对象传入。相反,它会作为其原始类型传递,那么只有在最终调用eventHandler(sender, args)时,它是否隐式转换为事件参数,如果事件具有注册人。

C类可能是这样的:

public class C 
{ 
    public event DataEventHandler<string> E; 
    public event DataEventHandler<C, string> EWithSender; 

    public void Go() 
    { 
     Console.WriteLine ("Calling event E..."); 

     E.Fire("hello"); 
     EWithSender.Fire(this, "hello"); 
    } 
} 

注意,在C事件的声明并没有明确自己使用DataEventHandler<DataEventArgs<string>>标记;这是隐式地由委托参数处理的。

调用代码可能看起来像:

C c = new C(); 
c.E += (args) => PrintOut(args); 
c.EWithSender += (sender, args) => Console.WriteLine("Sender Type: " + sender.GetType().Name + " -> Args: " + args.Args); 
c.Go(); 


private void PrintOut(string text) 
{ 
    Console.WriteLine(text); 
} 

再次,事件参数可以(但你没有)时隐式传递到方法转换回自己包裹的数据类型。

现在,这有一些缺点。主要是,在我看来,它有点违反标准的.NET EventHandler关于输入的做法,很难做出自己的事件参数等等。特别是因为我最终没有创建自己的EventArgs子类,而只是传递一些数据对象基本值类型或我自己的自定义类或数据模型)。它很好地服务我相当,但在实践中,我发现它越来越无用。我不主张这种风格/实现,但也许它会给你一些想法。

+0

感谢您花时间分享您的想法..发表这个问题值得! –