C# Unity 对于泛型接口的支持

来源:互联网 发布:淘宝数据采集器破解版 编辑:程序博客网 时间:2024/06/14 10:28

最近和小伙伴们在做一个前台小框架, 已经有些人用起来了。 但是之前框架没有引入 Ioc, 所以用户实现我们框架开放出去的接口后,我们只能通过反射来获取用户的实现类。这样一个问题就是我们虽然定义了接口, 但其实我们依然依赖于用户的实现,同时框架启动异常的慢。 所以我们打算使用开源 Ioc 框架来把完成依赖注入的动作。 我们就完全关心我们自己的接口, 然后用户可以自己把实现类注入到框架中来,这样不仅移除了依赖,同是也砍掉了反射,可以加快加载速度。

因为开放的接口中定义了泛型接口,所以选中支持泛型接口的 Unity 来完成 Ioc 功能。 下面一步步实现一个简单的 Unity 泛型接口实例。 

首先定义三个项目:

  1. UnityPrimer 框架代码
  2. UnityPrimerClient 框架用户代码
  3. UnityPrimerInterfaces 框架接口

项目结构如图:


框架接口包含:

  1. 实体接口
    1. [csharp] view plain copy
       在CODE上查看代码片派生到我的代码片
      1. namespace UnityPrimerInterfaces.Interfaces  
      2. {  
      3.     public interface IModel  
      4.     {  
      5.         string Name { getset; }  
      6.     }  
      7. }  


  2. 泛型接口
    1. [csharp] view plain copy
       在CODE上查看代码片派生到我的代码片
      1. namespace UnityPrimerInterfaces.Interfaces  
      2. {  
      3.     public interface IEnricher<T> where T:IModel  
      4.     {  
      5.         T StringToModel(string message);  
      6.         string ModelToString(T model);  
      7.     }  
      8. }  


框架用户实现代码:实现框架定义的接口,并且把实现实例注入到容器中。

  1. 接口实现
    1. [csharp] view plain copy
       在CODE上查看代码片派生到我的代码片
      1. public class Model :IModel  
      2.    {  
      3.        private string name;  
      4.        public string Name  
      5.        {  
      6.            get  
      7.            {  
      8.                return name;  
      9.            }  
      10.            set  
      11.            {  
      12.                name = value;  
      13.            }  
      14.        }  
      15.    }  


    2. [csharp] view plain copy
       在CODE上查看代码片派生到我的代码片
      1. public class Enricher<T> : IEnricher<T> where T:IModel  
      2.     {  
      3.         public T StringToModel(string message)  
      4.         {  
      5.             var model =new Model() { Name = message };  
      6.             return (T)((IModel)model);  
      7.         }  
      8.   
      9.         public string ModelToString(T model)  
      10.         {  
      11.             return model.Name;  
      12.         }  
      13.     }  


  2. 注册器
    1. [csharp] view plain copy
       在CODE上查看代码片派生到我的代码片
      1. namespace UnityPrimerClient  
      2. {  
      3.     public class Register  
      4.     {  
      5.         public static IUnityContainer Container;  
      6.         public static void Regist()  
      7.         {  
      8.             Container = new UnityContainer();  
      9.   
      10.             Container.RegisterType(typeof(IEnricher<>), typeof(Enricher<>), "Sample");  
      11.             //Container.RegisterType(typeof(IEnricher<IModel>), typeof(Enricher<Model>), "Sample");  
      12.             //Container.RegisterInstance<IEnricher<IModel>>(new Enricher<Model>());  
      13.         }  
      14.   
      15.     }  
      16. }  

框架代码:框架从容器中获取用户实现的对象,并进行操作。 

  1. [csharp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. namespace UnityPrimer  
    2. {  
    3.     class Program  
    4.     {  
    5.         static void Main(string[] args)  
    6.         {  
    7.             Register.Regist();  
    8.   
    9.             var enricher = Register.Container.Resolve<IEnricher<IModel>>("Sample");  
    10.   
    11.             IModel model = enricher.StringToModel("SampleModel");  
    12.             Console.WriteLine(enricher.ModelToString(model));  
    13.             Console.ReadLine();  
    14.   
    15.         }  
    16.     }  
    17. }  

对于 Unity 来说,最容易出错的部分,就是 register 和 resolve 的时候。同是对于泛型接口,泛型约束会导致resolve失败。例如把Enricher<T>中的 T 约束改为Model,则对于容器来说,它会认为,IModel不是一个合法的Model,那么在resolve时,把获取Enricher<Model>实例就会失败。


这个地方很容易疏忽。由于逆变的缘故,在resolve时,不能把Enricher<Model>转换为IEnricher<IModel>,所以需要告知容器我们需要的是一个IEnricher<IModel>对象,即Resolver<IEnricher<IModel>>("Sample");这样就可以返回我们需要的类型而无需强转。这时候就必须要注意Enricher<>的约束不能违反IEnricher<>接口的约束。


关于协变逆变。协变是外部告诉类说你这个方法应该返回什么类型。那么类的实现者会用一个泛型子类来作为泛型参数。这时候该方法可能会返回改泛型子类的参数。由于这个泛型子类一定是泛型父类的一个实例,所以协变通常不会造成以上这种类型强转的错误。但是对于逆变来说,实现类通过逆变告诉外部,我这个方法需要传递某个类的实例进来。这里的某个类很可能是一个泛型子类,因为实现者通常都会自己实现一个泛型子类来作为泛型类参数。这样由于类调用者通常会用泛型父类来作为泛型参数,就造成逆变类型强转冲突。

那么问题在于,通常我们需要的恰恰就是逆变转换,这就感觉到了语言的限制。有个方法不知道可不可以实现这个功能,就是TypeConverter

0 0
原创粉丝点击