2017-02-18 105 views
2

我在我的應用程序中有幾個Get____Factory方法,我想將它們與泛型整合,但我仍在調整C#和a)不是100%確定泛型是正確的方法去和b)我仍然在學習C#如何處理泛型。使用泛型映射接口類型到類

我最終會有一個工廠界面及其類的字典/映射。我不僅希望將我所有的工廠整合到一個易於訪問的方法中,而且還需要允許插件作者以自己的方式進行註冊(並以此方式訪問它們)。

我開始是這樣的:

注:最終將有一本字典或方式接口類型映射到它們的實現 - 的的if/else條件是醜陋的和暫時的,但一個簡單的方法來測試。

public T GetFactory<T>() where T : IFactory { 
    var t = typeof(T); 

    if (t.Equals(typeof(IRecipeFactory))) { 
     var factory = new RecipeFactory(); 
     return factory; 
    } 

    else if (t.Equals(typeof(IItemFactory))) { 
     var factory = new ItemFactory(); 
     return factory; 
    } 

    else if (t.Equals(typeof(ITileFactory))) { 
     var factory = new TileFactory(); 
     return factory; 
    } 
} 

它與Cannot implicitly convert type 'RecipeFactory' to 'T'失敗,所以這是行不通的。從長遠來看,我不會有條件,而是會按照類型查找班級。但是,只有找到解決方案才能解決投射問題,兩者都無法解決問題。

根據其他答案,我嘗試了雙重鑄造((T) (object)),但與InvalidCastException: Cannot cast from source type to destination type.錯誤。

要麼這是一個糟糕的架構,要麼我錯誤地使用泛型。

+0

你爲什麼要返回一個泛型類型,但是,你有一個特定類型的對象在return語句!? – niceman

+0

我想如果你把2個或更多的'Get ___ Factory'方法的例子,那麼也許我們可以幫助 – niceman

+0

正如我在文章中提到的,我想提供一個工廠接口到他們實例的映射。我可能會結束十幾個SomethingFactory類。該方法暴露給插件,這些插件沒有直接訪問實現,只有接口。我會添加更多的例子 – helion3

回答

1

由於該方法返回T,您將要在出口的路徑上將對象投射到T。要做到這一點,你投將不得不作出factoryIFactory

public T GetFactory<T>() where T : IFactory 
{ 
    var t = typeof(T); 

    if (t.Equals(typeof(IRecipeFactory))) 
    { 
     IFactory factory = new RecipeFactory(); 
     return (T)factory; 
    } 

    if (t.Equals(typeof(IItemFactory))) 
    { 
     IFactory factory = new ItemFactory(); 
     return (T)factory; 
    } 

    if (t.Equals(typeof(ITileFactory))) 
    { 
     IFactory factory = new TileFactory(); 
     return (T)factory; 
    } 

    throw new InvalidOperationException("Type not supported"); 
} 
+0

我很早就試過了,失敗了,但是由於不同的錯誤,我沒有意識到它失敗了,不是因爲我錯誤地投射了。 – helion3

+0

這是功能性的,但有大量的冗餘代碼,需要添加每種類型的特定情況,如果插件創建向一般社區開放,這絕對不會起作用(至少不是優雅)。我知道這只是OP代碼的一個固定版本,但請參閱我的答案以獲得更合適的方法。 –

+0

你是對的。 OP使用的方法可能不是實現它的「最佳」方法。但是,OP的問題不是如何更好地完成,而是如何使這種方法得到應用。如果超出這個範圍,最好留給另一個問題,因爲它是一個完全不同的主題。 –

1

讓我先說,你實際上是在尋找被控制(IOC)框架的反轉的簡單版本。看看Ninject或類似的東西,因爲它的內核和綁定工廠幾乎就是你想要的。它甚至允許元數據的附件,因此您可以根據具體情況將相同的接口解析爲不同的實現,當您的數據層可能需要從Web數據源或緩存數據源中提取時,這非常有用,例如。大多數IOC框架還提供了遞歸依賴關係解決方案,這意味着當某些實例具有需要其他依賴關係的構造函數時,將根據可推斷的映射或默認映射始終在鏈下產生相同的依賴關係解決方案。

除此之外,要做自己想做的事情,您需要使用Activator.CreateInstance,它需要一個類型,並基於此創建一個新實例。你的字典映射正處於正確的軌道上。當你將這兩者聯繫在一起時,你不需要任何條件邏輯,而且你不需要事先知道或者關心被請求的類型。當你感覺舒適的時候,如果你願意的話,你實際上可以縮短依賴分辨率和實例化到一行。

這裏是一個完全工作的樣品(從我的測試中爲30秒)已經做了你要盡我的理解是什麼:

using System; 
using System.Collections.Generic; 

namespace Generics 
{ 
    // create some dummy interfaces and implementations. 
    // make sure everything inherits from the same type to allow for 
    // a generic return statement 
    public interface IFactory 
    { 
     void DoStuff(); 
    } 
    public interface IFactory1 : IFactory { } 
    public class Factory1 : IFactory1 
    { 
     public void DoStuff() 
     { 
      Console.WriteLine("Factory1"); 
     } 
    } 
    public interface IFactory2 : IFactory { } 
    public class Factory2 : IFactory2 
    { 
     public void DoStuff() 
     { 
      Console.WriteLine("Factory2"); 
     } 
    } 


    class Program 
    { 
     // create our binding mappings 
     IDictionary<Type, Type> bindings = new Dictionary<Type, Type>() 
      { 
       // expose a way for plugins/etc to add to this. that part is trivial. 
       {typeof(IFactory1), typeof(Factory1) }, 
       {typeof(IFactory2), typeof(Factory2) } 
      }; 

     // a method to actually resolve bindings based on expected types 
     public IFactory ResolveBinding<T>() where T : IFactory 
     { 
      Type requestedType = typeof(T); 
      if (requestedType != null && bindings.ContainsKey(requestedType)) 
      { 
       // use the activator to generically create an instance 
       return (T) Activator.CreateInstance(bindings[requestedType]); 
      } 

      return null; 
     } 

     // test it out 
     static void Main(string[] args) 
     { 
      Program demo = new Program(); 
      // test with two interfaces 
      demo.ResolveBinding<IFactory1>().DoStuff(); // prints out "Factory1" 
      demo.ResolveBinding<IFactory2>().DoStuff(); // prints out "Factory2" 
      Console.ReadKey(); 
     } 
    } 
} 
1

下面是來自南卡羅的解決方案的解決方案有所不同。

public static class FactoryService 
{ 
    private static readonly Dictionary<Type, Func<IFactory>> factories = new Dictionary<Type, Func<IFactory>>() 
    { 
     { typeof(IRecipeFactory),() => new RecipeFactory() }, 
     { typeof(IItemFactory),() => new ItemFactory() }, 
     { typeof(ITileFactory),() => new TileFactory() } 
    }; 

    public static T GetFactory<T>() where T : IFactory 
    { 
     T factory = default(T); 
     Type requestedType = typeof(T); 

     if (factories.ContainsKey(requestedType)) 
     { 
      factory = (T)factories[requestedType].Invoke(); 
     } 

     return factory; 
    } 
} 

public interface IFactory { } 

public interface IRecipeFactory : IFactory { } 

public interface IItemFactory : IFactory { } 

public interface ITileFactory : IFactory { } 

public class RecipeFactory : IRecipeFactory { } 

public class ItemFactory : IItemFactory { } 

public class TileFactory : ITileFactory { } 

然後你使用這樣的:

IRecipeFactory rf = FactoryService.GetFactory<IRecipeFactory>();