2011-02-25 126 views
17

我有一個接口IExample,以及一組類ClassOne,ClassTwoClassThree,它們全部在不同的名稱空間中定義。我可能會刪除其中任何一個類,或者在一個新的地方添加一個新的類,在開發的後期階段。實例化實現特定接口的所有類

現在,我想找到在運行時實現IExample的所有類型,並將它們實例化爲。 (我事先知道沒有實現IExample的類將不需要任何構造函數參數,但我不知道如何在代碼中指定它,所以它是我 - 不是編譯器 - 知道...)

這是可能?我該如何去做呢?

更新:現在我已經試過幾個方法的建議,但對所有的人,行Activator.CreateInstance(type),我得到一個system.missingMethodException而因爲我「不能創建接口的實例。」這是我的代碼:

var tasks = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(a => a.GetTypes()) 
    .Where(t => typeof(IBootstrapperTask).IsAssignableFrom(t)) 

    // This line is where it fails 
    .Select(t => Activator.CreateInstance(t) as IBootstrapperTask) 

    .ToArray(); 
new AutoMapperBootstrapper(tasks).Initialize(); 

沒有as條款我沒有看到任何異常,但我給出的object[],我需要一個IBootstrapperTask[]對在摘錄的最後一行的構造。我嘗試了各種方法來投射它,但似乎沒有任何工作。

回答

28

這可以使用反射來完成。例如

var interfaceType = typeof(IExample); 
var all = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(x => x.GetTypes()) 
    .Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract) 
    .Select(x => Activator.CreateInstance(x)); 

注意:這隻會造成從當前AppDomain加載的程序集的IExample實例。這可能與當前進程中加載​​的所有程序集不同。但是,對於許多類型的應用程序,這將是等同的。

+4

更重要的是,它只包含已經被加載的組件。可能有其他的已被引用*但尚未加載。我個人更喜歡把它全部明確:) –

0

嘗試使用反射來獲取實現IExample的類型。此示例查看當前組件,但可以輕鬆地通過其他組件:

 IEnumerable<Type> types = Assembly.GetExecutingAssembly() 
      .GetTypes().Where(t=>t.GetInterfaces().Where(tt==typeof(IExample)).Count()>0); 
     List<object> objects = new List<object>(); 
     foreach (Type type in types) 
     { 
      objects.Add(Activator.CreateInstance(type)); 
     } 
+0

爲什麼不直接調用'Contains'?而且,這不會處理繼承。 – SLaks

+1

這會失敗,因爲我無法創建接口的實例 - 我必須創建實現它們的類型的實例。 –

+0

這不會失敗!我在類型上調用't.GetInterfaces()'。我在一個項目中使用了類似的東西。 – Aliostad

8

你需要知道組件的列表中查找,但隨後LINQ使得它相對容易:

var instances = (from assembly in assemblies 
       from type in assembly 
       where !type.IsAbstract && 
         type.IsClass && 
         type.IsPublic && 
         !type.IsGenericType && 
         typeof(IExample).IsAssignableFrom(type) 
       let ctor = type.GetConstructor(Type.EmptyTypes) 
       where ctor != null && ctor.IsPublic 
       select (IExample) Activator.CreateInstance(type)) 
       .ToList(); 

您可能會想到一些其他的限制增加,但他們很容易表示:)

instances然後將是List<IExample>

編輯:我懷疑我的代碼將在你沒有的地方工作,因爲我特別排除非類。我的猜測是你的代碼試圖實例化接口本身,即當ttypeof(IBootstrapperTask)

+0

感謝您的回覆 - 請參閱我的更新以獲取更多信息。 –

+0

@Tomas:編輯。你試過我的解決方案嗎? (如果需要,可以將最後一行改爲「ToArray」。)無可否認,我不明白爲什麼'as'會對原始代碼產生任何影響... –

+0

謝謝。當我仔細查看代碼時,我注意到了公有,非抽象和構造函數的明確要求,並在我的代碼中實現了它們。現在這個特定的部分可以工作,但是我有其他(可能是非相關的)問題,所以我無法看到這個實際工作,或者只是拋出了一個不同的異常。 –

5

你是否需要這種動態發生?有沒有什麼時候你可能有一個你不想創造的?在我看來,這是依賴注入(可以配置)的好例子。例如Unity有一個ResolveAll方法。

從上面的鏈接;

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>(); 
+0

僅僅在我的問題中給出了有限的示例信息,這是一個有效的參數,但在這種情況下DI不適用。 –

+0

哈 - 只是因爲我很快解僱DI,事實證明這是我無論如何都要結束的地方...... P謝謝!但是,我不會將其標記爲答案,僅僅因爲它沒有真正回答這個問題(「我如何找到所有實現此接口的類動態地?」),儘管它解決了我的實際問題。 –

+0

+1非常感謝!我一直在使用Caliburn.Micro構建一個項目來處理所有與MVVM相關的事情,而且我從來沒有想到我可以用它來解決這個問題!任何喜歡這個解決方案的人都應該嘗試這個框架。 – CuddleBunny

相關問題