使用泛型将接口类型映射到类

时间:2017-02-18 17:07:55

标签: c# generics

我的应用程序中有几个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.

要么这是一个糟糕的架构,要么我错误地使用了泛型。

3 个答案:

答案 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>();