• 4.1 反射工具


    在编写开发框架的时候,经常会使用反射。反射的作用主要是动态创建类型的实例,或者获取对象类型并动态调用方法、属性、字段等。

    我们在以前的.net framework框架中,反射工具包含了许多方法,但是在.net core中由于appdomain等变化的原因,许多方法已不再使用。我只将重反射工具类(ReflectionHelper)重要的几个方法说明一下。

    在框架编写过程中,我们会遇到这样的需求:找出应用所用到的所有程序集和类,然后进行下一步的处理。

    例如,我们有一个通用控件类BaseControl,各种富文本编辑器控件(RichText)、表格控件(Table)、分页控件(Paganitation)等都继承于通用控件类BaseControl。甚至CMS这个项目的评论(Comment)等控件也会继承该通用控件类BaseControl。我们需求是要做一个下拉列表,列出所有的控件。因为各个子控件会分散在不同的程序集中,评论控件就在CMS程序集中,这样我们必然会搜索当前应用中的所有程序集,从中找出所有继承于BaseControl的控件子类。这就是控件的列表。(如果我懒病不发作,能够写的够久的话,自定义表单、自定义查询等技术点可以看到这个需求。)

    下面的方法是找到所有的应用程序集:

     1         private static IEnumerable<Assembly> GetAssemblies()
     2         {
     3             List<Assembly> assemblies = new List<Assembly>();
     4 
     5             //以下2行,总是认为所有的个人程序集都依赖于core
     6             Type type = typeof(ReflectionHelper);
     7 
     8             var libs = DependencyContext.Default.CompileLibraries;
     9             foreach (CompilationLibrary lib in libs)
    10             {
    11                 //if (lib.Name.StartsWith("Microsoft") || lib.Name.StartsWith("System") || lib.Name.Contains(".System.") || lib.Name.StartsWith("NuGet") || lib.Name.StartsWith("AutoMapper")) continue;
    12                 if (lib.Serviceable) continue;
    13                 if (lib.Type == "package") continue;
    14 
    15                 var assembly = Assembly.Load(new AssemblyName(lib.Name));
    16                 assemblies.Add(assembly);
    17 
    18                 //以下,总是认为所有的个人程序集都依赖于core
    19 
    20                 ////过滤掉“动态生成的”
    21                 //if (assembly.IsDynamic) continue;
    22 
    23                 //if (assembly.FullName == type.AssemblyQualifiedName.Replace(type.FullName + ", ", ""))
    24                 //{
    25                 //    assemblies.Add(assembly);
    26                 //    continue;
    27                 //}
    28 
    29                 //if (assembly.GetReferencedAssemblies().Any(ass => ass.FullName == type.AssemblyQualifiedName.Replace(type.FullName + ", ", "")))
    30                 //{
    31                 //    assemblies.Add(assembly);
    32                 //}
    33             }
    34 
    35             return assemblies;
    36         }

    此处有个假设,第6行Type type = typeof(ReflectionHelper)。其中ReflectionHelper是核心应用程序集中的一个静态类,而核心应用程序集假设会被所有的应用程序集所引用。如果该假设不成立,需要将19-22行的注释去掉,针对每个找到的程序集获取所有引用的程序集。

    if (lib.Serviceable) continue;和if (lib.Type == "package") continue; 这两行的意思是排除所有的系统程序集、Nuget下载包,减少搜索范围,提高效率。(这2行暂未最终确认。)

    通过上面的程序,我们就可以从应用中找出所有的程序集。下一步从这些程序集中获取所有继承于BaseControl的控件子类。因为控件子类继承于BaseControl,因此子类所在的应用程序集必然引用BaseControl的应用程序集。从父类获取所有子类的方法如下:

     1         #region 类型搜索
     2         /// <summary>
     3         /// 获取子类型
     4         /// </summary>
     5         /// <param name="type">父类型</param>
     6         /// <returns></returns>
     7         public static IEnumerable<Type> GetSubTypes(Type type)
     8         {
     9             var assemblies = _Assemblies.Where(a =>
    10             {
    11                 Assembly assembly = type.GetTypeInfo().Assembly;
    12                 //基类所在程序集或依赖于基类的其他程序集
    13                 return a.FullName == assembly.FullName || a.GetReferencedAssemblies().Any(ra => ra.FullName == assembly.FullName);
    14             });
    15 
    16             TypeInfo typeInfo = type.GetTypeInfo();
    17 
    18             return assemblies.SelectMany(a =>
    19             {
    20                 return a.GetTypes().Where(t =>
    21                 {
    22                     if (type == t)
    23                     {
    24                         return false;
    25                     }
    26 
    27                     TypeInfo tInfo = t.GetTypeInfo();
    28 
    29                     if (tInfo.IsAbstract || !tInfo.IsClass || !tInfo.IsPublic)
    30                     {
    31                         return false;
    32                     }
    33 
    34                     if (typeInfo.IsGenericTypeDefinition)
    35                     {
    36                         return type.IsAssignableFromGenericType(t);
    37                     }
    38 
    39                     return type.IsAssignableFrom(t);
    40                 });
    41             });
    42         }
    43 
    44         /// <summary>
    45         /// 获取子类型
    46         /// </summary>
    47         /// <typeparam name="T">父类型</typeparam>
    48         /// <returns></returns>
    49         public static IEnumerable<Type> GetSubTypes<T>()
    50         {
    51             return GetSubTypes(typeof(T));
    52         }
    53         #endregion

    其中_Assemblies是从GetAssemblies()方法返回的结果。

    这样就能够获取当前的子类列表IEnumerable<Type>。对于我们的需求,可以这样写ReflectionHelper.GetSubTypes<BaseControl>()。但是该方法的结果是IEnumerable<Type>,是Type的列表。我们如果用下拉列表展示,应该展示的是中文名称,总不能显示类似namespace.classname, assemblyname的样子吧,这样会被客户骂的。应该下拉出来的是中文名,例如富文本编辑器、文件上传、分页、自动完成等。

    简单的做法是在BaseControl中增加一个抽象的Name属性,各个子类实现时override这个属性,标识该控件的中文名,倒是可以实现,不过在获取Name属性时,必须要实例化各个子类,天知道子类的构造函数有哪些参数。

    我们的做法是建一个TypeNameAttribute,标识在各个子控件类上。具体实现如下:

      1     /// <summary>
      2     /// 子类中,甚至TypeName,包括中英文及属性,以便反射使用
      3     /// </summary>
      4     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
      5     public class TypeNameAttribute : Attribute
      6     {
      7         private string _Code, _Name, _Description;
      8         /// <summary>
      9         /// 英文
     10         /// </summary>
     11         public string Code
     12         {
     13             get
     14             {
     15                 return _Code;
     16             }
     17             set
     18             {
     19                 _Code = value;
     20             }
     21         }
     22         /// <summary>
     23         /// 中文
     24         /// </summary>
     25         public string Name
     26         {
     27             get
     28             {
     29                 return _Name;
     30             }
     31             set
     32             {
     33                 _Name = value;
     34             }
     35         }
     36         /// <summary>
     37         /// 描述
     38         /// </summary>
     39         public string Description
     40         {
     41             get
     42             {
     43                 return _Description;
     44             }
     45             set
     46             {
     47                 _Description = value;
     48             }
     49         }
     50         /// <summary>
     51         /// 构造函数
     52         /// </summary>
     53         /// <param name="code">英文</param>
     54         /// <param name="name">中文</param>
     55         /// <param name="description">描述</param>
     56         public TypeNameAttribute(string code, string name, string description)
     57         {
     58             this._Code = code;
     59             this._Name = name;
     60             this._Description = description;
     61         }
     62 
     63         /// <summary>
     64         /// 构造函数
     65         /// </summary>
     66         /// <param name="code">英文</param>
     67         /// <param name="name">中文</param>
     68         public TypeNameAttribute(string code, string name) : this(code, name, string.Empty)
     69         {
     70         }
     71     }
     72 
     73     /// <summary>
     74     /// TypeName的工具类
     75     /// </summary>
     76     public static class TypeNameHelper
     77     {
     78         public static ConcurrentDictionary<Type, List<TypeNameHelperInfo>> list = new ConcurrentDictionary<Type, List<TypeNameHelperInfo>>();
     79 
     80         public static TypeNameHelperInfo GetTypeNameHelperInfo<T>(string code)
     81         {
     82             List<TypeNameHelperInfo> list = GetTypeNameHelperList<T>();
     83 
     84             return list.SingleOrDefault(info => info.Code == code);
     85         }
     86 
     87         /// <summary>
     88         /// 根据基类,获取所有子类的TypeName
     89         /// </summary>
     90         /// <typeparam name="T">基类型</typeparam>
     91         /// <returns>子类的TypeName信息</returns>
     92         public static List<TypeNameHelperInfo> GetTypeNameHelperList<T>()
     93         {
     94             if (list.ContainsKey(typeof(T)))
     95             {
     96                 return list[typeof(T)];
     97             }
     98 
     99             List<TypeNameHelperInfo> result = new List<TypeNameHelperInfo>();
    100 
    101             IEnumerable<Type> typeList = ReflectionHelper.GetSubTypes<T>();
    102 
    103             foreach (Type type in typeList)
    104             {
    105                 try
    106                 {
    107                     TypeNameAttribute attribute = ReflectionHelper.GetCustomAttribute<TypeNameAttribute>(type);
    108                     result.Add(new TypeNameHelperInfo()
    109                     {
    110                         Code = attribute.Code,
    111                         Name = attribute.Name,
    112                         Description = attribute.Description,
    113                         Type = type
    114                     });
    115                 }
    116                 catch
    117                 {
    118                 }
    119             }
    120 
    121             list[typeof(T)] = result;
    122 
    123             return result;
    124         }
    125     }
    126 
    127     /// <summary>
    128     /// TypeName的信息类
    129     /// </summary>
    130     public class TypeNameHelperInfo
    131     {
    132         /// <summary>
    133         /// 英文
    134         /// </summary>
    135         public string Code { get; set; }
    136         /// <summary>
    137         /// 中文
    138         /// </summary>
    139         public string Name { get; set; }
    140         /// <summary>
    141         /// 描述
    142         /// </summary>
    143         public string Description { get; set; }
    144         /// <summary>
    145         /// 类型
    146         /// </summary>
    147         public Type Type { get; set; }
    148     }

    例如自动完成控件就可以如下写法:

    1     /// <summary>
    2     /// 自动填充下拉框控件
    3     /// </summary>
    4     [TypeName("AutoComplete", "自动填充下拉框")]
    5     public class AutoComplete : BaseControl
    6     {
    7     ...
    8     }

     最终就可以通过TypeNameHelper.GetTypeNameHelperList<BaseControl>()就可以获取所有的控件子类,子类列表存放在List<TypeNameHelperInfo>,绑定到select标签即可

    面向云的.net core开发框架目录

  • 相关阅读:
    Eureka 原理圖
    RabbitMQ 介紹
    rabbitmq-3.5.1-安裝
    MyBatis 基础入门
    CSS
    程序员必会算法-KMP算法
    编程这些年
    排序算法之直接插入排序
    排序算法之选择排序
    排序算法之冒泡排序
  • 原文地址:https://www.cnblogs.com/BenDan2002/p/5996724.html
Copyright © 2020-2023  润新知