代码之家  ›  专栏  ›  技术社区  ›  Denis

在Unity中不指定泛型类型就可以注册泛型接口吗?

  •  0
  • Denis  · 技术社区  · 9 年前

    假设我有以下几点:

     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>>()
    

    3 回复  |  直到 9 年前
        1
  •  1
  •   George Alexandria    9 年前

    如果我正确理解你的问题,你可以使用 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>>();
    

    IDataTranslator<A, B> 具有 就是上面,你得到一个 DataTranslator<A, B> 然后得到 IdentityDataTranslator<A> IDataTranslator<A, A>

        2
  •  1
  •   Randy Levy    9 年前

    这里有一个稍微不同的方法来回答你的问题。与其完全关注注册,不如考虑如何解析到正确的类型。

    IDataTranslator<TFrom, TTo> 到a DataTranslator<TFrom, TTo> 。下一步是创建Unity容器扩展,以映射特例,其中TFrom在解析时与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>

    最后,设置容器并运行一些测试,以确保获得 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>));
    

    上述方法可行,但可能有一种严格基于注册的方法,我没有想到这也可行,并强制执行您的约束。

        3
  •  1
  •   George Alexandria    9 年前

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

    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;
            }
        }
    }