C#反射知识汇总
一、看清类型GetType(),GetProperties(),GetValue()
反射就是让程序在运行时认识“自己结构”的能力。
using System;
using System.Reflection;
public class Printer
{
public static void PrintObject(object obj, int indent = 0)
{
if (obj == null)
{
Console.WriteLine(new string(' ', indent) + "null");
return;
}
Type type = obj.GetType();
// 只处理属性
foreach (var prop in type.GetProperties())
{
var value = prop.GetValue(obj);
// 格式化输出
string output;
if (value == null)
{
output = "null";
}
else if (value is string)
{
output = $"\"{value}\"";
}
else if (value.GetType().IsClass && value.GetType() != typeof(string))
{
// 递归打印复杂对象,一层缩进
Console.WriteLine(new string(' ', indent) + $"{prop.Name}:");
PrintObject(value, indent + 2);
continue;
}
else
{
output = value.ToString();
}
Console.WriteLine(new string(' ', indent) + $"{prop.Name}: {output}");
}
}
}
// 测试类
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public Address Addr { get; set; }
}
class Program
{
static void Main()
{
var user = new User
{
Name = "小浮",
Age = 30,
Addr = new Address { City = "北京", Street = "中关村" }
};
Printer.PrintObject(user);
}
}
输出
Name: "小浮" Age: 30 Addr: City: "北京" Street: "中关村"
二、操作类型CreateInstance创建对象(就是反射版的new)、Invoke调用方法、Attribute读标签
动态创建对象、动态调用方法、访问私有成员、Attribute特性(给代码贴标签)
// 完整格式:类名, 程序集名
Type type = Type.GetType("MyApp.Models.User, MyApp.Models");
// 从配置或外部输入得到类名
string[] classNames = { "User", "Order" };
foreach (var name in classNames)
{
Type type = Type.GetType(name);
object obj = Activator.CreateInstance(type);//运行时按名字创建对象。
Console.WriteLine($"创建了: {obj.GetType().Name}");
}
Invoke不带参数
void RunPlugin(object plugin)
{
Type type = plugin.GetType();
// 找到名为 "Execute" 的方法
MethodInfo method = type.GetMethod("Execute");
// 调用它
method.Invoke(plugin, null);//反射慢的原因:Invoke 的代价是性能——它需要在运行时检查参数类型、处理权限、做安全校验
}
Invoke带参数
public class MathTool
{
public int Add(int a, int b) => a + b;
}
//使用
MathTool tool = new MathTool();
Type type = tool.GetType();
MethodInfo method = type.GetMethod("Add");
// 参数用 object 数组传入
object result = method.Invoke(tool, new object[] { 3, 5 });
Console.WriteLine((int)result); // 输出: 8
public class BankAccount
{
private decimal _balance = 1000m;
}
//使用
BankAccount account = new BankAccount();
Type type = account.GetType();
// BindingFlags 告诉反射"我要找什么样的成员"
FieldInfo field = type.GetField("_balance",
BindingFlags.NonPublic | BindingFlags.Instance);
decimal balance = (decimal)field.GetValue(account);
Console.WriteLine(balance); // 输出: 1000
Attribute特效:Attribute = 给代码打备注标签,运行时反射读取标签内容实现扩展逻辑;
[Description("用户姓名")]
public string Name { get; set; }
//也可自定义一个Attribute
[AttributeUsage(AttributeTargets.Property)]//AttributeTargets.Property这个标签只能贴在属性上
public class DescriptionAttribute : Attribute
{
public string Text { get; }
public DescriptionAttribute(string text)
{
Text = text;
}
}
//使用
public class User
{
[Description("用户姓名")]//命名惯例,省Attribute
public string Name { get; set; }
[Description("用户年龄")]
public int Age { get; set; }
public string Password { get; set; } // 没有标签
}
//用反射读取 Attribute
Type type = typeof(User);
foreach (var prop in type.GetProperties())
{
var attr = prop.GetCustomAttribute<DescriptionAttribute>();
if (attr != null)
Console.WriteLine($"{prop.Name}: {attr.Text}");
else
Console.WriteLine($"{prop.Name}: (无描述)");
}
AttributeTargets可以附加到哪些元素上
AttributeTargets.Class 类 AttributeTargets.Method 方法 AttributeTargets.Property 属性 AttributeTargets.Field 字段 AttributeTargets.Parameter 参数 AttributeTargets.All 全部
三、优化
慢在这里:
type.GetMethod("Execute"):遍历当前类型所有方法,按名字匹配,遍历父类型(如果没有找到),检测重载。
object result = method.Invoke(tool, new object[] { 3, 5 }); //检查method是否为null、检查调用者有没有权限,检查参数是否匹配、参数类型装箱拆箱(invoke的参数和返回值都是object)。
可以用BenchmarkDotNet看代码差距
优化
1.缓存(非热路径)
// ✅ 好做法:缓存 MethodInfo
var method = typeof(MyType).GetMethod("Execute");
foreach (var item in list)
{
method.Invoke(item, null);
}
2.表达式树(把反射编译成委托)
浙公网安备 33010602011771号