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开发框架目录

posted @ 2016-10-26 14:06 BenDan2002 阅读(...) 评论(...) 编辑 收藏