C# 反射

C# 中的反射(Reflection) 是 .NET 提供的一种强大机制,允许程序在运行时检查、分析和操作类型(类、结构、接口、方法、属性等)的元数据,并动态创建对象、调用方法、访问字段或属性。它主要通过 System.Reflection 命名空间中的类实现。


一、反射的核心用途

  1. 动态加载程序集(Assembly)
  2. 获取类型信息(Type)
  3. 创建对象实例(Activator / ConstructorInfo)
  4. 调用方法(MethodInfo.Invoke)
  5. 读写属性或字段(PropertyInfo / FieldInfo)
  6. 检查特性(Attribute)
  7. 实现插件系统、ORM、序列化框架等

二、基础使用步骤

1. 获取 Type 对象

csharp 复制代码
// 方法1:通过 typeof
Type type1 = typeof(string);

// 方法2:通过对象的 GetType()
string str = "hello";
Type type2 = str.GetType();

// 方法3:通过类型全名从 Assembly 加载
Type type3 = Type.GetType("System.String");

// 方法4:从程序集加载
Assembly assembly = Assembly.LoadFrom("MyLibrary.dll");
Type type4 = assembly.GetType("MyNamespace.MyClass");

注意:Type.GetType("...") 只能加载当前应用域中已加载或 mscorlib/System 中的类型,跨程序集需指定完整名称(含 Assembly 信息)。


2. 创建对象实例

使用 Activator.CreateInstance
csharp 复制代码
Type type = typeof(Person);
object obj = Activator.CreateInstance(type); // 调用无参构造函数

// 带参数构造
object obj2 = Activator.CreateInstance(type, "Alice", 30);
使用 ConstructorInfo
csharp 复制代码
ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(string), typeof(int) });
object instance = ctor.Invoke(new object[] { "Bob", 25 });

3. 调用方法

csharp 复制代码
Type type = typeof(Calculator);
object calc = Activator.CreateInstance(type);

// 获取方法
MethodInfo method = type.GetMethod("Add");
// 调用实例方法
int result = (int)method.Invoke(calc, new object[] { 10, 20 });

// 调用静态方法
MethodInfo staticMethod = type.GetMethod("Multiply");
int staticResult = (int)staticMethod.Invoke(null, new object[] { 5, 6 });

如果方法是泛型,需先调用 MakeGenericMethod()


4. 访问属性(Property)

csharp 复制代码
Type type = typeof(Person);
object person = Activator.CreateInstance(type);

// 设置属性
PropertyInfo nameProp = type.GetProperty("Name");
nameProp.SetValue(person, "Charlie");

// 获取属性值
string name = (string)nameProp.GetValue(person);

5. 访问字段(Field)

csharp 复制代码
FieldInfo field = type.GetField("_age", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(person, 28);
int age = (int)field.GetValue(person);

注意:私有成员需要 BindingFlags.NonPublic


6. 获取所有成员

csharp 复制代码
Type type = typeof(MyClass);

// 所有公共方法
MethodInfo[] methods = type.GetMethods();

// 所有公共属性
PropertyInfo[] props = type.GetProperties();

// 所有字段(包括私有)
FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

// 所有构造函数
ConstructorInfo[] ctors = type.GetConstructors();

7. 检查和读取特性(Attribute)

csharp 复制代码
[DisplayName("用户实体")]
public class User { }

// 检查是否有某个 Attribute
bool hasAttr = type.IsDefined(typeof(DisplayNameAttribute), false);

// 获取 Attribute 实例
var attr = (DisplayNameAttribute)type.GetCustomAttribute(typeof(DisplayNameAttribute));
Console.WriteLine(attr.DisplayName); // 输出:"用户实体"

三、常用 BindingFlags 枚举值

标志 说明
Public 公共成员(默认)
NonPublic 私有/受保护成员
Instance 实例成员
Static 静态成员
DeclaredOnly 仅当前类型声明的成员(不包括继承)

示例:

csharp 复制代码
type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);

四、性能注意事项

  • 反射比直接调用慢很多(可能慢 10~100 倍),因为涉及类型查找、安全检查、装箱/拆箱等。
  • 避免在高频循环中使用反射
  • 可缓存 MethodInfo / PropertyInfo 等以提升性能。
  • 考虑使用委托(如 Delegate.CreateDelegate)或表达式树(Expression)优化
  • .NET Core / .NET 5+ 引入了 System.Reflection.Emitsource generator 等更高效替代方案。

五、实际应用场景举例

1. 通用对象拷贝器

csharp 复制代码
public static void CopyProperties(object source, object target)
{
    var sourceType = source.GetType();
    var targetType = target.GetType();
    
    foreach (var prop in sourceType.GetProperties())
    {
        var targetProp = targetType.GetProperty(prop.Name);
        if (targetProp != null && targetProp.CanWrite)
        {
            targetProp.SetValue(target, prop.GetValue(source));
        }
    }
}

2. 插件系统

csharp 复制代码
Assembly plugin = Assembly.LoadFrom("Plugin.dll");
Type pluginType = plugin.GetTypes().FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
IPlugin instance = (IPlugin)Activator.CreateInstance(pluginType);
instance.Execute();

3. ORM 映射(如将 DataTable 转为对象)

csharp 复制代码
public static T ToObject<T>(DataRow row) where T : new()
{
    T obj = new T();
    Type type = typeof(T);
    foreach (DataColumn col in row.Table.Columns)
    {
        PropertyInfo prop = type.GetProperty(col.ColumnName);
        if (prop != null && row[col] != DBNull.Value)
            prop.SetValue(obj, row[col]);
    }
    return obj;
}

六、总结

优点 缺点
高度灵活,支持运行时动态行为 性能开销大
实现通用框架(如 DI、AOP、序列化) 代码可读性降低
支持插件、脚本扩展 容易引发运行时异常(如拼写错误)
可用于调试、测试工具 不支持 AOT 编译(如 Native AOT)的部分场景

建议 :仅在必要时使用反射,并做好异常处理(如 NullReferenceExceptionTargetExceptionMissingMethodException 等)。


相关推荐
倦王1 分钟前
力扣日刷47
算法·leetcode·职场和发展
MicroTech20253 分钟前
突破量子数据加载瓶颈,MLGO微算法科技推出面向大规模量子计算的分治态制备技术
科技·算法·量子计算
码王吴彦祖5 分钟前
顶象 AC 纯算法迁移实战:从补环境到纯算的完整拆解
java·前端·算法
SccTsAxR10 分钟前
算法基石:手撕离散化、递归与分治
c++·经验分享·笔记·算法
wuweijianlove11 分钟前
算法测试中的数据规模与时间复杂度匹配的技术4
算法
yugi98783817 分钟前
C# 串口下载烧写BIN文件工具
开发语言·c#
Q741_14735 分钟前
每日一题 力扣 3655. 区间乘法查询后的异或 II 模拟 分治 乘法差分法 快速幂 C++ 题解
c++·算法·leetcode·模拟·快速幂·分治·差分法
The_Ticker36 分钟前
印度股票实时行情API(低成本方案)
python·websocket·算法·金融·区块链
夏乌_Wx40 分钟前
剑指offer | 2.4数据结构相关题目
数据结构·c++·算法·剑指offer·c/c++
开心码农1号44 分钟前
Java rabbitMQ如何发送、消费消息、全套可靠方案
java·rabbitmq·java-rabbitmq