反射机制到底能干啥
在写代码的时候,有时候你压根不知道某个类长啥样,但又得调它的方法或者读它的属性。比如做插件系统,主程序启动时动态加载外部DLL里的功能模块,这时候就不能写死类型。C#的反射机制就是干这个的——让你在运行时“看穿”一个对象的结构,动态调用它的一切成员。
就像你去修手机,没拆开之前不知道里面是啥型号的屏幕,但一打开就能看到零件编号,然后按编号找对应操作方式。反射就是那个“拆机”的过程。
获取类型信息的基本操作
每个类都有对应的Type对象,它是反射的入口。通过GetType或typeof都能拿到。
string text = "hello";
Type type = text.GetType();
Console.WriteLine(type.Name); // 输出 String
// 或者直接用 typeof
Type stringType = typeof(string);
Console.WriteLine(stringType.FullName);动态创建实例并调用方法
假设你有一个类User,但你在编码时不知道它是否存在,只有运行时才知道。可以用Assembly.Load结合Activator.CreateInstance来搞定。
using System.Reflection;
// 假设User类在当前程序集中
Type userType = Type.GetType("MyApp.User");
if (userType != null)
{
object user = Activator.CreateInstance(userType);
MethodInfo method = userType.GetMethod("SayHello");
method.Invoke(user, null); // 调用 SayHello 方法
}这招在配置驱动的逻辑中特别有用。比如后台管理系统根据配置文件决定加载哪个处理器类来处理订单,不同环境用不同的实现,不用改一行代码。
读写私有字段和属性
有些老项目里藏着一堆private字段,测试或兼容时非读不可。反射可以绕过访问限制。
FieldInfo field = userType.GetField("_password", BindingFlags.NonPublic | BindingFlags.Instance);
string pwd = (string)field.GetValue(user);
Console.WriteLine("密码是:" + pwd); // 实际开发中别真打印密码啊同理,SetField也能修改值。不过这种操作要小心,破坏封装容易埋坑,只在必要时用。
遍历所有公共方法
想看看一个类提供了哪些公开方法?用GetMethods就行。
MethodInfo[] methods = typeof(List<int>).GetMethods();
foreach (MethodInfo m in methods)
{
Console.WriteLine(m.Name + " | " + m.ReturnType.Name);
}输出会列出Add、Remove、ToArray这些熟悉的方法名,返回类型也一清二楚。调试工具、文档生成器常用这招。
实际应用场景举例
写一个通用的日志记录器,自动记录传入对象的所有属性值。不管来的是Order、Product还是Customer,都能照单全收。
public void LogObject(object obj)
{
Type t = obj.GetType();
PropertyInfo[] props = t.GetProperties();
Console.WriteLine($"【{t.Name}】详情:");
foreach (PropertyInfo p in props)
{
object value = p.GetValue(obj);
Console.WriteLine($"{p.Name} = {value}");
}
}调用LogObject(new Order { Id = 1001, Amount = 99.5 }),立马打出所有字段。省得每个类都重写ToString。
性能注意点
反射比直接调用慢不少,因为它要查表、解析元数据。高频路径上尽量避免频繁使用。如果必须用,可以把Type、MethodInfo缓存起来重复利用。
另外,用Emit或表达式树可以生成委托来提速,但那是进阶玩法了。日常够用就行,别一开始就过度优化。