2011-04-05 72 views
1

我已經閱讀了關於這個問題的幾篇文章,但沒有一個答案似乎適用於我。這裏的情況是 - 我有一個通用的功能,在其他類調用:如何將對象轉換爲自己的類型?

public class Dispatcher<T> where T : Event { 
    public void Notify<X>(X tEvent) where X : Event { 
     if (someField is IListener<X, T>) { 
      //this never executes--X is Event regardless of its derived type 
     } 
    } 
} 

和調用代碼:

public class Effect { 
    public Event myEvent; 

    public CallNotify() { 
     Dispatcher.Notify(myEvent); 
    } 
} 

的問題是,事件有幾十個派生類型的,我需要Notify()調用與派生類型X一起發生。到目前爲止,無論事件是否傳入,它都會調用Notify < Event>()。編譯的唯一解決方案是調用Notify(myEvent作爲DerivedEvent) ,但必須對每種類型進行硬編碼 - 不可接受。

如果我從派生類的實例中給它一個「this」指針,類似的函數會正確地推斷出它。

這裏肯定有一個使用反射的解決方案。

+0

您不能使用接口嗎? – 2011-04-05 16:00:32

+0

你能舉出更多的示例代碼嗎?我沒有看到OtherClass與Dispatcher的關係。編譯器會自動填充Notify方法的泛型參數爲Event,因爲myEvent是Event類型。你必須從別的地方調用它,它具有顯式類型的myEvent,用你想要的通用參數調用Notify方法 – BrandonAGr 2011-04-05 16:39:44

+0

Dispatcher是OtherClass,對不起。我編輯了這個問題。你很簡潔地解決問題。爲了從其他地方調用Notify(),我可能需要爲每個派生類編寫一個函數。我使用這種方法來實現類似的功能:註冊(this)工作得很好,從實現IListener 的每個類調用。但是那個代碼......似乎更加必要,不像樣板/開銷。我會嘗試這種方法 – MrEff 2011-04-05 17:37:20

回答

2

爲了調用它,你似乎期望你將不得不使用反射來生成myEvent的實際類型的方法。但我會強調,做下面的事情是一個糟糕的想法,可能意味着您的設計需要重新考慮。

MethodInfo openGenericMethod = OtherClass.GetType().GetMethod("Notify"); 
MethodInfo closedGenericMethod = openGenericMethod.MakeGenericMethod(myEvent.GetType()); 

closedGenericMethod.Invoke(OtherClass, new object[]{ myEvent });, 

我沒有實際測試上面的代碼,但它看起來類似的東西

+0

這看起來正是我想要的。我會測試它。 – MrEff 2011-04-05 17:44:31

0

你能不能刪除約束和檢查,以確保它獲得來自Event,像這樣:

public class Dispatcher<T> where T : Event { 
    public void Notify<X>(X tEvent) { 
     if(typeof(tEvent).IsSubclassOf(typeof(Event)) 
     { 
      if (someField is IListener<X, T>) { 
       //this never executes--X is Event regardless of its derived type 
      } 
     } 
    } 
} 
+0

我不這麼認爲。我將不得不在幾個地方更改代碼,並且會犧牲參數類型的編譯時檢查。即使那樣,我也不確定它會工作。 – MrEff 2011-04-05 16:35:21

2

你有兩個問題。

首先,您的仿製藥正在泄漏。無論何時您需要確定特定實例的類型,您的函數不再是通用的。考慮你的設計有缺陷並重新檢查你正在嘗試做什麼。

其次,IListener<X,Y>不是一種類型。泛型在.NET中不是泛型的;運行時確定應用程序需要的所有實際類型並創建它們。如果您在應用程序中實際使用該類型,則runtim將創建一個類型IListener<int,string>

var foo = new List<int>(); 
var bar = foo.GetType() == typeof(List<>); 

在這種examle,barfalse

即使所有這一切,是的,這是可能的。你只需要瞭解如何使用泛型類型定義進行反射。 This is a pretty good link at MSDN that explains how it works.

我強烈建議您重新考慮以這種方式使用泛型。有時抽象廣告荒唐不是最好的東西...

+0

運行時創建通用類型,而不是編譯器。這就是爲什麼你可以在運行時創建一個新的封閉泛型類型 – BrandonAGr 2011-04-05 16:37:15

+0

@BrandonAGr:是的。編輯。 – Will 2011-04-05 16:38:21

+0

嗯,我想這是一個泄漏,但它是一個小泄漏。我只需要知道派生類型來決定通知哪個IListener。我能夠看到的避免「泄漏」的唯一方法是爲每種類型的事件製作一個單獨的Dispatcher實例,這將打敗目的 - 爲客戶端編寫最少的代碼。我正在閱讀你的鏈接 – MrEff 2011-04-05 16:43:40

0

所以我似乎已經想通了,在另一端的解決方法。取而代之的

public class Dispatcher<T> where T : Event { 
    public void Notify<X>(X tEvent) where X : Event { 
     foreach (Object l in listeners) { 
      if (l is IListener<X, T>) { //never true 
       (l as IListener<X, T>).OnEvent(); 
      } 
     } 
    } 
} 

我有這樣的大雜燴:

public class Dispatcher<T> where T : Event { 
    public void Notify<X>(X tEvent) where X : Event { 
     foreach (Object l in listeners) { 
      foreach (Type t in l.GetType().GetInterfaces()) { 
       Type[] temp = t.GetGenericArguments(); 
       if (temp.Count() > 0 && temp[0] == tEvent.GetType()) { 
        MethodInfo mi = t.GetMethod("OnEvent", new Type[] {tEvent.GetType()}); 
        mi.Invoke(l, new object[] { tEvent }); 
       } 
      } 
     } 
    } 
} 

這似乎是工作,雖然我不喜歡測試每個接口(Windows窗體至少有10個接口)。我要去嘗試BrandonAGr的解決方案

+0

出於好奇,這裏'Event'是什麼類?我在'System'命名空間中看不到'Event'類。我看到'System.EventHandler'和'System.EventArgs'。我沒有看到'System.Event'。 「事件」是你自己編造的東西嗎?如果不是,那麼完全限定名稱空間名稱是什麼?用像'Event'這樣的通用容器對象來訪問包含的對象並直接檢查它的類型似乎應該是微不足道的,這可以減輕你在這裏試圖找出有關內部信封而不看任何內容。 – 2014-06-30 17:54:38

相關問題