我的应用程序中有几个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 :(得分:1)
由于方法返回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");
}
答案 1 :(得分: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();
}
}
}
答案 2 :(得分:1)
这里的解决方案与S.C。的解决方案不同。
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>();