反射和特性
反射(Reflection)
反射是C#中强大的机制,允许程序在运行时动态获取类型信息、操作对象或调用方法,而无需在编译时明确直到类型的定义。
-
动态类型操作
-
通过
Type
类获取类型信息(如typeof()
或obj.GetType()
),检查继承关系(IsSubclassOf
)、接口实现(IsAssignableFrom
)等。 -
动态创建实例:
Activator.CreateInstance
支持通过构造函数参数甚至私有构造方法实例化对象。 -
调用方法和属性:通过
MethodInfo.Invoke
执行方法,PropertyInfo.SetValue
设置属性值。 -
处理泛型:例如通过
MakeGenericType
创建泛型类实例,MakeGenericMethod
调用泛型方法。
-
-
程序集操作
-
加载外部程序集:
Assembly.LoadFrom
或Assembly.LoadFile
动态加载DLL。 -
遍历程序集的类型和成员:例如获取所有公共方法或私有字段。
-
-
性能与限制
-
反射虽灵活,但存在性能开销(因动态解析类型)和安全性风险(如访问私有成员破坏封装)。
-
适用场景:序列化、依赖注入、测试框架等需要动态行为的场景
-
使用方法
-
获取类型信息
通过
Type
类获取对象的类型元数据
Type type = typeof(int); // 通过类型名
Type type2 = myObj.GetType(); // 通过对象实例
-
动态创建对象
使用Activator.CreateInstance
创建实例
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type);
- 访问成员
获取属性、方法、字段信息
PropertyInfo[] props = type.GetProperties();
MethodInfo method = type.GetMethod("MyMethod");
- 动态调用方法
通过反射调用方法
MethodInfo method = type.GetMethod("Add");
int result = (int)method.Invoke(instance, new object[] { 2, 3 });
汇总使用场景
-
插件系统:动态加载程序集(DLL)并调用其功能。
-
序列化/反序列化:通过反射便利对象的属性生成JSON/XML。
-
ORM框架:将数据库记录映射到对象属性。
-
单元测试框架:自动发现并执行测试方法。
注意要点
反射操作通常比静态代码慢,需要慎重使用。
-
缓存
Type
或MethodInfo
对象。 -
使用
dynamic
关键字(动态类型)。 -
改用表达树(Expression Trees)或源代码生成器(Souree Generators)。
反射官方文档
属性和反射 - C# | Microsoft Learn
特性(Attributes)
特性是为代码元素(类、方法、属性等)添加元数据的标记,这些元数据可通过反射在运行时读取,用于控制程序行为或提供额外信息。
内置常用类型
-
[AttributeUsage] 约束自定义特性的应用目标(如类、方法)和是否允许多次应用。
-
[Obsolete] 标记方法或类已过时。
[Obsolete("Use NewMethod instead", error: true)] public void OldMethod() { }
-
[Serializable] 标记类可序列化。
-
[DllImport] 用于调用非托管代码。
- [Conditional] 条件编译(如调试代码):
[Conditional("DEBUG")] public void Log(string message) { }
自定义特性
- 自定特性类
继承Attribute类并添加元数据[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] public class AuthorAttribute : Attribute { public string Name { get; } public AuthorAttribute(string name) => Name = name; }
-
应用特性
将特性标记到目标代码元素[Author("John")] [Author("Jane", Version = 2.0)] // 允许通过属性传递参数 public class MyClass { }
- 通过反射读取特性
在运行时获取特性信息Type type = typeof(MyClass); var attributes = type.GetCustomAttributes(typeof(AuthorAttribute), false); foreach (AuthorAttribute attr in attributes) { Console.WriteLine($"作者: {attr.Name}"); }
汇总使用场景
-
验证逻辑:通过特性标记必填字段(类似
[Required]
)。
-
Web路由:ASP.NET Core中
[HttpGet]
、[Route]
等特性定义API端点。
-
权限控制:自定义
[Authorize]
特性限制访问权限。
-
文档生成:为Swagger等工具提供API描述信息(
[ApiExplorer]
)。
特性官方文档
属性和反射 - C# | Microsoft Learn
反射与特性的协同
两者结合可实现高度动态化的编程模式:
-
元数据驱动设计
-
特性提供结构化元数据(如
[Required]
验证标记),反射在运行时读取并执行逻辑(如自动验证字段)。 -
示例:ASP.NET MVC通过
[HttpGet]
等特性标记控制器方法,反射解析路由并调用对应方法。
-
-
动态代码生成与扩展
-
反射可动态生成类型(如Emit技术),特性则为生成的代码添加配置信息。
-
序列化库(如JSON.NET )利用特性控制序列化行为(如
[JsonPropertyName]
),反射遍历属性进行转换。
-
反射和特性优缺特点
技术 | 优点 | 缺点 |
---|---|---|
反射 | 动态性高、支持通用代码(如插件系统) | 性能低、破坏封装、代码可读性差 |
特性 | 声明式编程、简化配置、增强元数据 | 过度使用导致维护困难、兼容性依赖运行时版本 |
反射和特性使用方面
- 自动注册服务:通过扫描程序集中标记了`[Service]`特性的类,实现依赖注入。
- AOP(面向切面编程):通过特性标记需要拦截的方法,利用反射动态生成代理。
// 自定义特性标记Excel列索引 [AttributeUsage(AttributeTargets.Property)] public class ExcelColumnAttribute : Attribute { public int Index { get; } public ExcelColumnAttribute(int index) => Index = index; } public class Product { [ExcelColumn(0)] public string Name { get; set; } [ExcelColumn(1)] public decimal Price { get; set; } } // 反射读取特性并映射Excel数据 public static List<Product> ParseExcel(string filePath) { var products = new List<Product>(); var type = typeof(Product); foreach (var prop in type.GetProperties()) { var attr = prop.GetCustomAttribute<ExcelColumnAttribute>(); if (attr != null) { // 根据attr.Index读取Excel列并赋值 } } return products; }
反射和特性注意事项
-
安全性:反射可能绕过访问修饰符(如访问私有成员),需谨慎使用。
-
维护性:过度依赖反射会降低代码可读性,尽量通过接口或泛型替代。
-
性能敏感场景:避免在频繁执行的代码路径中使用反射。