2012-09-29 114 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

感謝您花時間分享您的想法..發表這個問題值得! –