本篇定位 这是 C# 反射系列的第二篇,我们学习如何通过反射动态调用方法和访问属性 。读完你会理解:
如何通过反射创建实例
如何动态调用方法
如何动态读写属性和字段
如何处理参数和返回值
反射调用的性能特点
1. 创建实例 使用 Activator.CreateInstance 最简单的方式是使用 Activator.CreateInstance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Person { public string Name { get ; set ; } public int Age { get ; set ; } public Person () { } public Person (string name, int age ) { Name = name; Age = age; } } object instance1 = Activator.CreateInstance(typeof (Person));Console.WriteLine(instance1 is Person); object instance2 = Activator.CreateInstance(typeof (Person), "Alice" , 30 );var person = (Person)instance2;Console.WriteLine($"{person.Name} , {person.Age} " ); Person instance3 = Activator.CreateInstance<Person>();
使用 ConstructorInfo 更细粒度的控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Type type = typeof (Person); ConstructorInfo ctor1 = type.GetConstructor(Type.EmptyTypes); object instance1 = ctor1.Invoke(null );ConstructorInfo ctor2 = type.GetConstructor(new [] { typeof (string ), typeof (int ) }); object instance2 = ctor2.Invoke(new object [] { "Bob" , 25 });ConstructorInfo[] constructors = type.GetConstructors(); foreach (var ctor in constructors){ var parameters = string .Join(", " , ctor.GetParameters().Select(p => p.ParameterType.Name)); Console.WriteLine($"构造函数: Person({parameters} )" ); }
2. 动态调用方法 基本调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Calculator { public int Add (int a, int b ) => a + b; public int Multiply (int a, int b ) => a * b; public void PrintResult (int result ) => Console.WriteLine($"结果: {result} " ); } Type type = typeof (Calculator); object calc = Activator.CreateInstance(type);MethodInfo addMethod = type.GetMethod("Add" ); object result = addMethod.Invoke(calc, new object [] { 5 , 3 });Console.WriteLine(result); MethodInfo printMethod = type.GetMethod("PrintResult" ); printMethod.Invoke(calc, new object [] { 42 });
处理参数 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 public class StringHelper { public string Concat (string a, string b ) => a + b; public string Repeat (string text, int count ) => string .Concat(Enumerable.Repeat(text, count)); public void ModifyByRef (ref int value ) => value *= 2 ; } Type type = typeof (StringHelper); object helper = Activator.CreateInstance(type);MethodInfo concatMethod = type.GetMethod("Concat" ); object result = concatMethod.Invoke(helper, new object [] { "Hello" , "World" });Console.WriteLine(result); MethodInfo repeatMethod = type.GetMethod("Repeat" ); object result2 = repeatMethod.Invoke(helper, new object [] { "ab" , 3 });Console.WriteLine(result2); MethodInfo refMethod = type.GetMethod("ModifyByRef" ); object [] parameters = new object [] { 10 };refMethod.Invoke(helper, parameters); Console.WriteLine(parameters[0 ]);
调用静态方法 1 2 3 4 5 6 7 8 9 10 11 12 public class MathHelper { public static int Square (int x ) => x * x; public static double Pi => Math.PI; } Type type = typeof (MathHelper); MethodInfo squareMethod = type.GetMethod("Square" ); object result = squareMethod.Invoke(null , new object [] { 5 });Console.WriteLine(result);
调用泛型方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class GenericHelper { public T GetDefault <T >() => default (T); public T[] CreateArray <T >(int length ) => new T[length]; } Type type = typeof (GenericHelper); object helper = Activator.CreateInstance(type);MethodInfo method = type.GetMethod("GetDefault" ); MethodInfo genericMethod = method.MakeGenericMethod(typeof (int )); object result = genericMethod.Invoke(helper, null );Console.WriteLine(result); MethodInfo arrayMethod = type.GetMethod("CreateArray" ); MethodInfo genericArrayMethod = arrayMethod.MakeGenericMethod(typeof (string )); object result2 = genericArrayMethod.Invoke(helper, new object [] { 3 });Console.WriteLine(((string [])result2).Length);
3. 动态访问属性 读取属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Product { public string Name { get ; set ; } public decimal Price { get ; set ; } } Type type = typeof (Product); var product = new Product { Name = "Laptop" , Price = 999.99 m };PropertyInfo nameProp = type.GetProperty("Name" ); object nameValue = nameProp.GetValue(product);Console.WriteLine(nameValue); PropertyInfo priceProp = type.GetProperty("Price" ); object priceValue = priceProp.GetValue(product);Console.WriteLine(priceValue);
设置属性 1 2 3 4 5 6 7 8 9 10 11 Type type = typeof (Product); var product = new Product();PropertyInfo nameProp = type.GetProperty("Name" ); nameProp.SetValue(product, "Phone" ); PropertyInfo priceProp = type.GetProperty("Price" ); priceProp.SetValue(product, 599.99 m); Console.WriteLine($"{product.Name} : {product.Price} " );
处理只读属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class ReadOnlyExample { public string Id { get ; } = Guid.NewGuid().ToString(); public string Name { get ; set ; } } Type type = typeof (ReadOnlyExample); var obj = new ReadOnlyExample();PropertyInfo idProp = type.GetProperty("Id" ); Console.WriteLine(idProp.CanRead); Console.WriteLine(idProp.CanWrite); try { idProp.SetValue(obj, "new-id" ); } catch (TargetParameterCountException ex){ Console.WriteLine("无法设置只读属性" ); }
4. 动态访问字段 读写字段 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 public class Config { public string ApiKey = "secret123" ; private int _timeout = 5000 ; } Type type = typeof (Config); var config = new Config();FieldInfo apiKeyField = type.GetField("ApiKey" ); object apiKey = apiKeyField.GetValue(config);Console.WriteLine(apiKey); apiKeyField.SetValue(config, "new-secret" ); Console.WriteLine(config.ApiKey); FieldInfo timeoutField = type.GetField("_timeout" , System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); object timeout = timeoutField.GetValue(config);Console.WriteLine(timeout); timeoutField.SetValue(config, 10000 ); Console.WriteLine(config._timeout);
5. 实战例子:通用对象复制器 让我们写一个通过反射实现的对象复制工具:
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 public class ObjectCloner { public static T Clone <T >(T source ) where T : class { if (source == null ) return null ; Type type = source.GetType(); T target = (T)Activator.CreateInstance(type); PropertyInfo[] properties = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); foreach (var prop in properties) { if (prop.CanRead && prop.CanWrite) { object value = prop.GetValue(source); prop.SetValue(target, value ); } } FieldInfo[] fields = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); foreach (var field in fields) { object value = field.GetValue(source); field.SetValue(target, value ); } return target; } } var original = new Person { Name = "Alice" , Age = 30 };var cloned = ObjectCloner.Clone(original);cloned.Name = "Bob" ; Console.WriteLine($"原始: {original.Name} " ); Console.WriteLine($"克隆: {cloned.Name} " );
6. 实战例子:简单的 ORM 查询映射 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 public class DataMapper { public static T MapFromDictionary <T >(Dictionary<string , object > data ) where T : class , new () { T instance = new T(); Type type = typeof (T); foreach (var kvp in data) { PropertyInfo prop = type.GetProperty(kvp.Key, System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); if (prop != null && prop.CanWrite) { try { object value = Convert.ChangeType(kvp.Value, prop.PropertyType); prop.SetValue(instance, value ); } catch { } } } return instance; } } var data = new Dictionary<string , object >{ { "Name" , "Charlie" }, { "Age" , 28 } }; var person = DataMapper.MapFromDictionary<Person>(data);Console.WriteLine($"{person.Name} , {person.Age} " );
7. 性能考虑 反射的性能开销 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 public class PerformanceTest { public static void Main () { var person = new Person { Name = "Test" , Age = 25 }; const int iterations = 1 _000_000; var sw = System.Diagnostics.Stopwatch.StartNew(); for (int i = 0 ; i < iterations; i++) { var name = person.Name; } sw.Stop(); Console.WriteLine($"直接访问: {sw.ElapsedMilliseconds} ms" ); Type type = typeof (Person); PropertyInfo prop = type.GetProperty("Name" ); sw.Restart(); for (int i = 0 ; i < iterations; i++) { var name = prop.GetValue(person); } sw.Stop(); Console.WriteLine($"反射访问: {sw.ElapsedMilliseconds} ms" ); } }
性能优化建议
缓存 Type、PropertyInfo、MethodInfo
1 2 3 4 5 6 7 8 9 10 11 12 13 for (int i = 0 ; i < 1000 ; i++){ var prop = typeof (Person).GetProperty("Name" ); prop.GetValue(person); } PropertyInfo prop = typeof (Person).GetProperty("Name" ); for (int i = 0 ; i < 1000 ; i++){ prop.GetValue(person); }
使用表达式树替代反射
避免频繁的反射调用
在初始化时使用反射
在热路径中使用缓存或编译的委托
总结
创建实例 :使用 Activator.CreateInstance 或 ConstructorInfo.Invoke
调用方法 :使用 MethodInfo.Invoke,注意处理参数和返回值
访问属性 :使用 PropertyInfo.GetValue/SetValue
访问字段 :使用 FieldInfo.GetValue/SetValue
性能 :反射有开销,需要缓存元数据
应用 :对象复制、ORM 映射、序列化等
下一篇我们将学习**特性(Attributes)**的定义和使用。