繁体   English   中英

是否可以注册通用接口而无需在Unity中指定通用类型?

[英]Is it possible to register a generic interface without specifying generic types in Unity?

假设我有以下几点:

 public interface IDataTranslator<TFrom, TTo>  {
        TTo Translate(TFrom fromObj);
    }

  public class IdentityDataTranslator<T> : IDataTranslator<T, T> {
        public T Translate(T fromObj) {
            return fromObj;
        }
    }

我正在寻找一种可以做的方法:

   IdentityDataTranslator<A> trA = UnityContainer.Resolve<IDataTranslator<A, A>>()
   IdentityDataTranslator<B> trB = UnityContainer.Resolve<IDataTranslator<B, B>>()
   IdentityDataTranslator<C> trc = UnityContainer.Resolve<IDataTranslator<C, C>>()

对于所有这些解析(假设我不知道将要解析的泛型的类型),我希望Unity返回具有适当泛型的IdentityDataTranslator实例。 是否可以通过某种方式向Unity注册以实现此目的?

您可以通过创建一个继承具有两个泛型参数的原始接口的新接口来匹配该接口与类之间的泛型参数的数量。 假定已知这两个参数始终相同。

using Microsoft.Practices.Unity;

namespace ConsoleApp
{
    class Program
    {
        static IUnityContainer container;
        static void Main(string[] args)
        {
            container = new UnityContainer();
            container.RegisterType(typeof(IDataTranslator<>), typeof(IdentityDataTranslator<>));

            IdentityDataTranslator<string> translator = container.Resolve<IDataTranslator<string>>() as IdentityDataTranslator<string>;
            string result = translator.Translate("12351");
        }
    }

    public interface IDataTranslator<TFrom, TTo>
    {
        TTo Translate(TFrom fromObj);
    }

    public interface IDataTranslator<T> : IDataTranslator<T,T>
    {
    }

    public class IdentityDataTranslator<T> : IDataTranslator<T>
    {
        public T Translate(T fromObj)
        {
            return fromObj;
        }
    }
}

如果我正确理解您的问题,则可以使用InjectionFactory设置如何解析当前类型。 看起来像这样:

    // It's a test implementation of IDataTranslator<From, To>
    public class DataTranslator<TFrom, TTo> : IDataTranslator<TFrom, TTo>
    {
        TTo IDataTranslator<TFrom, TTo>.Translate(TFrom fromObj)
        {
            throw new NotImplementedException();
        }
    }

    ...
    var strResolve = "notSameResolve";
    container.RegisterType(typeof(IDataTranslator<,>), typeof(DataTranslator<,>), strResolve);
    container.RegisterType(typeof(IDataTranslator<,>),
        new InjectionFactory(
            (con, type, str) =>
            {
                var argumets = type.GetGenericArguments();
                if (argumets[0] != argumets[1])
                {
                    return con.Resolve(type, strResolve);
                }

                return con.Resolve(typeof(IdentityDataTranslator<>).MakeGenericType(type.GetGenericArguments()[0]));
            }));

    var trA = (IdentityDataTranslator<A>)container.Resolve<IDataTranslator<A, A>>();
    var trAData = (DataTranslator<A, B>)container.Resolve<IDataTranslator<A, B>>();

因此,如果尝试使用上面的InjectionFactory解析IDataTranslator<A, B> ,则尝试使用相同的参数解析IDataTranslator<A, A>时,将获得DataTranslator<A, B>并获得IdentityDataTranslator<A>

这是您问题的略有不同的方法。 除了完全专注于注册之外,我们还考虑如何解析为正确的类型。

这个想法是注册将IDataTranslator<TFrom, TTo>映射到DataTranslator<TFrom, TTo>的典型情况。 下一步是创建一个Unity容器扩展,以映射在解析IDataTranslator<TFrom, TTo>与TTo类型相同的特殊情况。

鉴于:

public class A { }

public class B { }

public interface IDataTranslator<TFrom, TTo>
{
    TTo Translate(TFrom fromObj);
}

public class DataTranslator<TFrom, TTo> : IDataTranslator<TFrom, TTo>
{
    public TTo Translate(TFrom fromObj)
    {
        return Activator.CreateInstance<TTo>();
    }
}

public class IdentityDataTranslator<T> : IDataTranslator<T, T>
{
    public T Translate(T fromObj)
    {
        return fromObj;
    }
}

接下来创建一个容器扩展来处理IdentityDataTranslator:

public class IdentityGenericsExtension : UnityContainerExtension
{
    private readonly Type identityGenericType;
    private readonly Type baseType;

    public IdentityGenericsExtension(Type identityGenericType, Type baseType)
    {
        // Verify that Types are open generics with the correct number of arguments
        // and that they are compatible (IsAssignableFrom).
        this.identityGenericType = identityGenericType;
        this.baseType = baseType;
    }

    protected override void Initialize()
    {
        this.Context.Strategies.Add(
            new IdentityGenericsBuildUpStrategy(this.identityGenericType, this.baseType),
                UnityBuildStage.TypeMapping);
    }

    private class IdentityGenericsBuildUpStrategy : BuilderStrategy
    {
        private readonly Type identityGenericType;
        private readonly Type baseType;

        public IdentityGenericsBuildUpStrategy(Type identityGenericType, Type baseType)
        {
            this.identityGenericType = identityGenericType;
            this.baseType = baseType;
        }

        public override void PreBuildUp(IBuilderContext context)
        {
            if (context.OriginalBuildKey.Type.IsGenericType &&
                context.OriginalBuildKey.Type.GetGenericTypeDefinition() == this.baseType)
            {
                // Get generic args
                Type[] argTypes = context.BuildKey.Type.GetGenericArguments();

                if (argTypes.Length == 2 && argTypes.Distinct().Count() == 1)
                {
                    context.BuildKey = new NamedTypeBuildKey(
                        this.identityGenericType.MakeGenericType(argTypes[0]),
                        context.BuildKey.Name);
                }
            }
        }
    }
}

这是在类型映射检查之前执行的操作,以检查请求的类型是否为IDataTranslator<T,K> ,并且有两个通用参数,并且两个通用参数都属于同一类型。 如果是这样,则将IdentityDataTranslator<T>设置为新的构建密钥(而不是预期的DataTranslator<T,K>

缺少对类型的验证,以确保它们是正确的形状并可以分配。

最后,设置容器并运行一些测试以确保当from和to类型相同时我们得到一个IdentityDataTranslator

var container = new UnityContainer();
container.AddExtension(
    new IdentityGenericsExtension(typeof(IdentityDataTranslator<>), typeof(IDataTranslator<,>)));

container.RegisterType(typeof(IDataTranslator<,>), typeof(DataTranslator<,>));

// Since A is different than B we get back a DataTranslator<A,B>
var dataTranslator = container.Resolve<IDataTranslator<A, B>>();
Debug.Assert(dataTranslator.GetType() == typeof(DataTranslator<A, B>));

// Since A is the same as A we get back a IdentityDataTranslator<A>
var identityTranslator = container.Resolve<IDataTranslator<A, A>>();
Debug.Assert(identityTranslator.GetType() == typeof(IdentityDataTranslator<A>));

上面的方法行得通,但是可能有严格的基于注册的方法,我没有想到这也是行得通的,并且强加了您的约束。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM