.net基础--反射
1、什么是反射
元数据是指程序以及类型信息的数据,它包含在程序集中(程序集包含:元数据,编译代码,资源)。
在.net中反射是查看和操作元数据的动作。
2、为什么要用反射
- 在程序运行时,动态加载需要的程序集,以便操作其元数据。(例如有些IOC容器的实现,就是通过配置对应程序集,在运行时动态加载这些程序集,实现注入)
- 构造出可重用性的代码,通过反射可以动态的绑定/获取数据(例如orm中将查询结果转成实体,在更新数据时需要获取实体信息构造更新语句)。
3、.net中使用反射常用的类型
1)Assembly
当我们需要加载程序集时,可以通过Assembly.LoadFrom,或者Assembly.LoadFile方法加载指定程序集。有了程序集我们就可以获取程序集中类型了。
2)Type
Type(类型),我们可以通过Assembly.GetType获取指定的类型,也可以通过Assembly.GetTypes获取程序集中所有的类型。
当然我们也可以通过GetType方法获取变量的类型,还可以使用typeof获取指定类型。
Type中常用方法(以下方法都有对应根据名称获取相应类型的方法,也是很常用,此处略过):
上面列举的方法都有相应重载方法,通过BindingFlags(使用位运算)指定获取对应的类型
例如获取实例非公有方法:
var privateMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
BindingFlags(以下仅列举常用的,详情可以参考BindingFlags枚举信息)
//仅类型内部声明的(不包含父类型的) DeclaredOnly = 2, //实例成员 Instance = 4, //静态成员 Static = 8, //公有 Public = 16, //非公有 NonPublic = 32, ...
简单代码示例
1 var assembly = Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "QinGY.DotnetCoreStudy.SimpleNetCore.dll")); 2 var types = assembly.GetTypes(); 3 var type = types.FirstOrDefault(p => p.Name == "SaleOrder"); 4 // var type = assembly.GetType("SaleOrder"); 5 if (type == null) 6 { 7 throw new Exception("SaleOrder不存在"); 8 } 9 else 10 { 11 #region 示例--列举Type中的类型 12 var constructors = type.GetConstructors(); 13 14 Console.WriteLine($"总共有{constructors.Length}个构造方法"); 15 16 var events = type.GetEvents(); 17 if (events != null) 18 { 19 foreach (var eventItem in events) 20 { 21 Console.WriteLine($"事件:{eventItem.Name}"); 22 } 23 } 24 25 var properties = type.GetProperties(); 26 foreach (var propertyItem in properties) 27 { 28 Console.WriteLine($"属性类型:{propertyItem.PropertyType} 属性名称:{propertyItem.Name}"); 29 } 30 var fields = type.GetFields(); 31 foreach (var fieldItem in fields) 32 { 33 Console.WriteLine($"字段类型:{fieldItem.FieldType} 字段名称:{fieldItem.Name}"); 34 } 35 36 var members = type.GetMembers(); 37 foreach (var memberItem in members) 38 { 39 Console.WriteLine($"成员类型:{memberItem.MemberType} 成员名称:{memberItem.Name}"); 40 } 41 42 var methods = type.GetMethods(); 43 foreach (var methodItem in methods) 44 { 45 Console.WriteLine($"方法名称:{methodItem.Name}"); 46 } 47 48 var privateMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic); 49 foreach (var privateMethodItem in privateMethods) 50 { 51 Console.WriteLine($"非公有方法名称:{privateMethodItem.Name}"); 52 } 53 #endregion 54 // 创建对象 55 var instance = Activator.CreateInstance(type); 56 //指定参数构造函数创建对象 57 object[] parameters = new object[] { 58 "amz0001",DateTime.Now,1 }; 59 var instance2 = Activator.CreateInstance(type, parameters); 60 //调用方法 61 var method = type.GetMethod("GetOrderInfo", BindingFlags.Instance | BindingFlags.Public); 62 method.Invoke(instance2, null); 63 //获取/设置属性 64 var pro = type.GetProperty("OrderNumber"); 65 var val = pro.GetValue(instance2); 66 Console.WriteLine("ordernumber:" + val.ToString()); 67 pro.SetValue(instance2, "W00001"); 68 val = pro.GetValue(instance2); 69 Console.WriteLine("修改后ordernumber:" + val.ToString()); 70 71 }
反射泛型方法,需要调用MakeGenericMethod指定参数实际类型。
--泛型------------------- public class Manager { public void Process<T>(T t) where T : BaseRequestDto { Console.WriteLine("请求访问....获取数据"); } } ---调用---------------- var newManager = Activator.CreateInstance(typeof(Manager)); var method = typeof(Manager).GetMethod("Process"); //method.Invoke(newManager, null); ///直接调会出现异常!!! var gMethod = method.MakeGenericMethod(typeof(ProductRequestDto)); gMethod.Invoke(newManager, null);
在实际项目中用到的关于反射例子
一、.netcore中依赖注入业务服务
1 #region 服务注入 2 Assembly assembly = Assembly.Load("qingy.Service"); 3 List<Type> types = assembly.GetTypes().Where(p => p.IsClass && !p.IsAbstract && !p.IsGenericType 4 && p.GetInterfaces().Any(x => x == typeof(IService)) 5 ).ToList(); 6 foreach (var item in types) 7 { 8 services.AddTransient(item); 9 10 } 11 #endregion
二、通过反射获取枚举的描述信息
1 public static Dictionary<string, string> GetEnumDescription(Type enumType) 2 { 3 var result = new Dictionary<string, string>(); 4 var fields = enumType.GetFields(); 5 foreach (var item in fields) 6 { 7 if (item.FieldType.IsEnum) 8 { 9 object[] descriptionAttributes = item.GetCustomAttributes(typeof(DescriptionAttribute), false); 10 result.Add(item.Name, descriptionAttributes.Length < 1 ? item.Name : ((DescriptionAttribute)descriptionAttributes[0]).Description); 11 } 12 } 13 return result; 14 }
三、使用反射构造实体映射(略)
4、反射性能非常差?不要用反射?
待定
特性(Attribute)
特性(Attribute),是指.net公共语言运行时允许添加类似关键字的声明信息。它可以声明在类、属性、方法等元数之上。
特性一般都是为元素添加附加的关键信息,通常是通过反射得到这些附件信息进行对应的处理。
常见的特性:Serializable,JsonProperty,MaxLength,DllImport等等。
自定义特性
[AttributeUsage(AttributeTargets.All,AllowMultiple =false,Inherited =false)] public class CommentAttribute:Attribute { /// <summary> /// 备注 /// </summary> public string CommentString { get; set; } /// <summary> /// 长度 /// </summary> public int Length { get; set; } }
说明:
1、特性必须继承自Attribute;
2、在特性类之上添加AttributeUsage可以定义特性的使用范围(通过AttributeTargets限定声明的元素;通过AllowMultiple限定是否可以多次声明,通过Inherited限定继承关系)
3、使用[]包住声明到对应的元素之上即可。
注:自定义特性一般都是通过反射获取信息并处理(代码略)