[英]Using generics to map interface types to classes
我的應用程序中有幾個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'
失敗,因此這將不起作用。 從長遠來看,我將沒有條件,但寧願按其類型查找該類。 但是,在我無法找到演員表問題的解決方案之前,這兩種方法都不起作用。
根據其他答案,我嘗試了雙InvalidCastException: Cannot cast from source type to destination type.
( (T) (object)
),但是InvalidCastException: Cannot cast from source type to destination type.
出現此錯誤InvalidCastException: Cannot cast from source type to destination type.
。
要么這是一個糟糕的體系結構,要么我使用了不正確的泛型。
由於該方法返回T
您將在出站時將對象強制轉換為T
為此,您必須將factory
為IFactory
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");
}
首先讓我說,您實際上正在看的是控制反轉(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();
}
}
}
這里的解決方案與SC的解決方案有點不同。
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>();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.