反射(Reflection)是 .NET 运行时提供的元数据内省能力。通过反射,可以在运行时获取类型信息、调用成员、创建实例,甚至动态生成代码。特性(Attribute)则是向程序实体附加元数据的声明式工具。两者密切配合,是实现序列化、依赖注入、ORM、AOP 等高级框架的技术基石。
反射核心 API
1 2 3 4 5 6 7 8 9 10 11 12
| using System.Reflection;
Type type1 = typeof(string); Type type2 = "hello".GetType(); Type type3 = Type.GetType("System.String");
foreach (MethodInfo method in type1.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { Console.WriteLine($"{method.ReturnType.Name} {method.Name}"); }
|
动态创建类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| interface IPlugin { string Execute(string input); }
public class UppercasePlugin : IPlugin { public string Execute(string input) => input.ToUpper(); }
Type pluginType = Assembly.Load("MyApp").GetType("MyApp.UppercasePlugin"); IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType)!; Console.WriteLine(plugin.Execute("hello"));
|
运行时调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Calculator { public int Add(int a, int b) => a + b; private int Multiply(int a, int b) => a * b; }
Type calcType = typeof(Calculator); object calc = Activator.CreateInstance(calcType)!;
MethodInfo addMethod = calcType.GetMethod("Add", new[] { typeof(int), typeof(int) })!; int result = (int)addMethod.Invoke(calc, new object[] { 3, 4 })!;
MethodInfo mulMethod = calcType.GetMethod("Multiply", BindingFlags.NonPublic | BindingFlags.Instance)!; int secret = (int)mulMethod.Invoke(calc, new object[] { 3, 4 })!;
|
自定义特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] public class AuthorAttribute : Attribute { public string Name { get; } public int Priority { get; set; }
public AuthorAttribute(string name) => Name = name; }
[Author("张三", Priority = 1)] [Author("李四", Priority = 2)] public class SampleService { [Author("王五")] public void DoWork() { } }
|
运行时读取特性
1 2 3 4 5 6 7 8
| public static void PrintAuthorInfo(Type type) { var authors = type.GetCustomAttributes<AuthorAttribute>(); foreach (var author in authors) { Console.WriteLine($"{type.Name}: {author.Name} (优先级: {author.Priority})"); } }
|
条件特性(Conditional)
1 2 3 4 5 6 7 8 9 10 11
| #define DEBUG using System.Diagnostics;
public class Logger { [Conditional("DEBUG")] public static void Log(string message) { Console.WriteLine($"[DEBUG] {message}"); } }
|
当 DEBUG 符号未定义时,所有对 Log 的调用在编译时被移除——这与普通条件语句不同,调用方甚至连方法调用的 IL 都不会生成。
反射与泛型
1 2 3 4 5 6 7 8 9 10 11 12
| class GenericDemo<T> { public T? Value { get; set; } }
Type genericType = typeof(GenericDemo<>); Type closedType = genericType.MakeGenericType(typeof(int)); object instance = Activator.CreateInstance(closedType)!;
var prop = closedType.GetProperty("Value"); prop!.SetValue(instance, 42); Console.WriteLine(prop.GetValue(instance));
|
特性在框架中的应用
1. 验证框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| [AttributeUsage(AttributeTargets.Property)] public class RequiredAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Property)] public class RangeAttribute : Attribute { public int Min { get; } public int Max { get; } public RangeAttribute(int min, int max) => (Min, Max) = (min, max); }
public class UserModel { [Required] public string Name { get; set; } = "";
[Range(1, 120)] public int Age { get; set; } }
public static List<string> Validate<T>(T obj) { var errors = new List<string>(); foreach (var prop in typeof(T).GetProperties()) { var value = prop.GetValue(obj); if (prop.GetCustomAttribute<RequiredAttribute>() != null && (value == null || string.IsNullOrWhiteSpace(value?.ToString()))) { errors.Add($"{prop.Name} 是必填项"); }
var range = prop.GetCustomAttribute<RangeAttribute>(); if (range != null && value is int num && (num < range.Min || num > range.Max)) { errors.Add($"{prop.Name} 必须在 {range.Min}-{range.Max} 之间"); } } return errors; }
|
2. 配置文件映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| [AttributeUsage(AttributeTargets.Property)] public class ConfigKeyAttribute : Attribute { public string Key { get; } public ConfigKeyAttribute(string key) => Key = key; }
public static T MapFromConfig<T>(IConfiguration config) where T : new() { var obj = new T(); foreach (var prop in typeof(T).GetProperties()) { var attr = prop.GetCustomAttribute<ConfigKeyAttribute>(); if (attr != null && config[attr.Key] is string value) { prop.SetValue(obj, Convert.ChangeType(value, prop.PropertyType)); } } return obj; }
|
总结:反射赋予程序内省自身结构的能力,特性则为元数据提供声明式表达。两者结合,催生了 .NET 生态中大量的现代化框架。反射虽然有性能开销(方法调用比直接调用慢 10-100 倍),但在启动时结合缓存、emit、Source Generator 等技术可以极大缓解。在需要元编程的地方,反射是 C# 开发者手中不可或缺的利器。