[英]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.