C# 反射 浅入浅出
什么是反射 (Reflection)
C# 的 反射 (Reflection) 是运行时读取、检查和操作类型(类、接口、结构)、成员(方法、属性、字段、构造函数)以及属性(Attribute)的一组 API,位于 System.Reflection 命名空间。简单说:反射能让程序在运行时“看见”自己和别的类型,并可以动态地创建对象、调用方法、读写属性、读取特性信息等。
常见用途
- 动态创建对象(例如插件/依赖注入、序列化/反序列化)。
- 运行时调用方法或访问属性(例如脚本、测试框架、ORM)。
- 读取/处理自定义 Attribute(例如 MVC 的路由、验证特性)。
- 构建工具或元编程(代码生成、检查、文档)。
API & 简单示例
Type: 类型信息
// 获取类型信息, 并创建实例
Type type1 = typeof(Sample);
object instance1 = Activator.CreateInstance(type1)!;
GetMethods: 获取所有方法
// 列出所有公共方法和静态方法
foreach (var m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
{
Console.WriteLine($" - {m.Name} (IsStatic={m.IsStatic}, Return={m.ReturnType.Name})");
}
GetMethod: 获取指定方法
// 获取 Print 方法, 传递 Hello 参数并调用
MethodInfo? print = type1.GetMethod("Print");
print?.Invoke(instance1, new object?[] { "Hello" });
GetProperty: 获取指定属性
// 获取 PublicProperty 属性, 并获取/设置值
PropertyInfo? prop = type1.GetProperty("PublicProperty");
Console.WriteLine($"{prop?.Name}: {prop?.GetValue(instance1, null)}");
prop?.SetValue(instance1, "C# is the best language!");
Console.WriteLine($"Updated Property Value: {prop?.Name}");
GetProperties: 获取所有属性
PropertyInfo[] props = type1.GetProperties();
Console.WriteLine("All Properties:");
foreach (var p in props)
{
Console.WriteLine($" - {p.Name} (Type: {p.PropertyType.Name})");
}
GetField: 获取指定字段
// 获取 PrivateField 字段, 并获取/设置值
FieldInfo? field = type1.GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"Private Field Value: {field?.GetValue(instance1)}");
field?.SetValue(instance1, 100);
Console.WriteLine($"Updated Private Field Value: {field?.GetValue(instance1)}");
GetFields: 获取所有字段
// 获取所有私有字段, 并列出
FieldInfo[] fields = type1.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine("All Private Fields:");
foreach (var f in fields)
{
Console.WriteLine($" - {f.Name} (Type: {f.FieldType.Name})");
}
ConstructorInfo: 获取指定构造函数
// 获取指定构造函数, 传递 Parameterized Constructor Value 参数并创建实例
ConstructorInfo? ctor = type1.GetConstructor(new [] { typeof(string) });
object? instance2 = ctor?.Invoke(new object[] { "Parameterized Constructor Value" });
Console.WriteLine($"instance2.PublicProperty: {prop?.GetValue(instance2, null)}");
MakeGenericMethod: 创建泛型方法
// 获取 Echo(int/string) 方法, 创建泛型方法并调用
MethodInfo? echoMethod = type1.GetMethod("Echo");
MethodInfo? echoInt = echoMethod?.MakeGenericMethod(typeof(int));
MethodInfo? echoString = echoMethod?.MakeGenericMethod(typeof(string));
var echoIntResult = echoInt?.Invoke(instance1, new object?[] { 123 });
var echoStringResult = echoString?.Invoke(instance1, new object?[] { "Hello Generic" });
Console.WriteLine($"Echo<int>: {echoIntResult}");
Console.WriteLine($"Echo<string>: {echoStringResult}");
GetCustomAttributes: 获取指定特性
// 列出所有 DemoAttribute 特性
object[] attributes = type1.GetCustomAttributes(typeof(DemoAttribute), false);
foreach (DemoAttribute attr in attributes)
{
Console.WriteLine($"Custom Attribute - DemoAttribute Name: {attr.Name}");
}
性能优化
直接用 MethodInfo.Invoke 每次都要做很多检查。更快的办法是把 MethodInfo 转为委托(CreateDelegate)或使用表达式树生成委托(可处理非 public 或复杂签名)。
示例:把实例方法转为委托:
var printDelegate =
(Action<Sample, string>)Delegate.CreateDelegate(typeof(Action<Sample, string>), null, print);
printDelegate((Sample)instance1, "via delegate");
更优做法:针对已知签名创建具体委托类型(见上面 Delegate.CreateDelegate 示例),或者使用 Expression 构建并编译一个委托(可以零拷贝传参,性能接近直接调用)。
完整例子
namespace Learn;
using System;
using System.Reflection;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DemoAttribute : Attribute
{
public string Name { get; set; }
public DemoAttribute(string name) => Name = name;
}
[Demo("Sample Class")]
public class Sample
{
public string PublicProperty { get; set; } = "I'm Public Property Value!";
private int _privateField = 42;
public Sample() {}
public Sample(string publicProperty) => PublicProperty = publicProperty;
public void Print(string content)
{
Console.WriteLine($"Print: {content}");
}
private string Secret()
{
return $"Private field value is {_privateField}";
}
public T Echo<T>(T input)
{
return input;
}
public static string StaticMethodToUpper(string s) => s.ToUpper();
}
class Program
{
static void Main(string[] args)
{
// 1) Get Type
Type type1 = typeof(Sample);
object instance1 = Activator.CreateInstance(type1)!;
// 2) List all public method
Console.WriteLine("Public Methods: ");
foreach (var m in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
{
Console.WriteLine($" - {m.Name} (IsStatic={m.IsStatic}, Return={m.ReturnType.Name})");
}
// 3) Call the public method
MethodInfo? print = type1.GetMethod("Print");
print?.Invoke(instance1, new object?[] { "Hello" });
// 4) Access the property
PropertyInfo? prop = type1.GetProperty("PublicProperty");
Console.WriteLine($"{prop?.Name}: {prop?.GetValue(instance1, null)}");
prop?.SetValue(instance1, "C# is the best language!");
Console.WriteLine($"Updated Property Value: {prop?.Name}");
PropertyInfo[] props = type1.GetProperties();
Console.WriteLine("All Properties:");
foreach (var p in props)
{
Console.WriteLine($" - {p.Name} (Type: {p.PropertyType.Name})");
}
print?.Invoke(instance1, new object?[] { prop?.GetValue(instance1, null) });
// 5) Access the private field
FieldInfo? field = type1.GetField("_privateField", BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"Private Field Value: {field?.GetValue(instance1)}");
field?.SetValue(instance1, 100);
Console.WriteLine($"Updated Private Field Value: {field?.GetValue(instance1)}");
FieldInfo[] fields = type1.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine("All Private Fields:");
foreach (var f in fields)
{
Console.WriteLine($" - {f.Name} (Type: {f.FieldType.Name})");
}
print?.Invoke(instance1, new object?[] { Convert.ToString(field?.GetValue(instance1)) });
// 6) Construct with parameterized constructor
ConstructorInfo? ctor = type1.GetConstructor(new [] { typeof(string) });
object? instance2 = ctor?.Invoke(new object[] { "Parameterized Constructor Value" });
Console.WriteLine($"instance2.PublicProperty: {prop?.GetValue(instance2, null)}");
// 7) Call the generic method
MethodInfo? echoMethod = type1.GetMethod("Echo");
MethodInfo? echoInt = echoMethod?.MakeGenericMethod(typeof(int));
MethodInfo? echoString = echoMethod?.MakeGenericMethod(typeof(string));
var echoIntResult = echoInt?.Invoke(instance1, new object?[] { 123 });
var echoStringResult = echoString?.Invoke(instance1, new object?[] { "Hello Generic" });
Console.WriteLine($"Echo<int>: {echoIntResult}");
Console.WriteLine($"Echo<string>: {echoStringResult}");
// 8) Call the static method
MethodInfo? staticMethodToUpper = type1.GetMethod("StaticMethodToUpper", BindingFlags.Public | BindingFlags.Static);
var staticResult = staticMethodToUpper?.Invoke(null, new object?[] {"Test Static Call"});
Console.WriteLine($"Static Method Result: {staticResult}");
// 9) Access custom attribute
object[] attributes = type1.GetCustomAttributes(typeof(DemoAttribute), false);
foreach (DemoAttribute attr in attributes)
{
Console.WriteLine($"Custom Attribute - DemoAttribute Name: {attr.Name}");
}
// 10) Load from dll, example here.
// Assembly assembly = Assembly.LoadFrom("ExternalLibrary.dll");
// Type? externalType = assembly.GetType("ExternalLibrary.ExternalClass");
// object? externalInstance = Activator.CreateInstance(externalType);
// 11) Performance enhancement
var printDelegate =
(Action<Sample, string>)Delegate.CreateDelegate(typeof(Action<Sample, string>), null, print);
printDelegate((Sample)instance1, "via delegate");
}
}
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Swaggy Macro
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果

