反射是.NET框架中一个强大的特性,允许程序在运行时检查和操作类型信息。通过反射,开发者可以动态地创建对象、调用方法、访问属性等,为程序提供了极大的灵活性。本文将详细讲解C#反射的使用方法及其应用场景。
System.Reflection
命名空间中。在程序运行时通过 System.Reflection 命名空间提供的 API,可完成如下操作:
反射,简单来说,就是程序在运行时能够自我检查,获取类型、方法、字段等元数据信息的能力。
反射的核心功能依赖于 System.Reflection
命名空间中的类型,其中包含 Type
、Assembly
、MethodInfo
等关键类。
System.Reflection
:反射的核心功能
System.Type
:代表任意类型(如类、接口、枚举等),可通过typeof
获取类型信息。基本列举反射实际应用时所涉及的所有类与命名空间
System.Reflection(命名空间)
|
├── Assembly // 程序集操作
├── Module // 模块信息
├── ConstructorInfo // 构造函数
├── ParameterInfo // 参数信息
├── MethodInfo // 方法信息
├── PropertyInfo // 属性信息
├── MemberInfo // 成员信息
├── FieldInfo // 字段信息
├── TypeInfo // 类型信息
└── MethodBase // 方法基类信息
|
System(命名空间)
|
├── Type // 类型元数据
├── Activator // 实例创建
└── AppDomain // 应用程序域管理
反射常用类与方法速查表
类/方法 | 用途 | 示例代码 |
---|---|---|
Type |
获取类型元数据 | typeof(MyClass) |
Assembly |
加载和操作程序集 | Assembly.Load("MyAssembly") |
MethodInfo |
获取和调用方法 | method.Invoke(obj, args) |
PropertyInfo |
访问属性值 | property.GetValue(obj) |
BindingFlags |
控制成员可见性(如私有) | BindingFlags.NonPublic |
Activator.CreateInstance |
动态创建对象实例 | Activator.CreateInstance(type) |
动态创建对象并调用方法
// 获取类型
Type type = typeof(MyClass);
// 动态创建实例
object instance = Activator.CreateInstance(type);
// 获取方法并调用
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(instance, null); // 输出 "Hello, World!"
Type type = typeof(string); // 获取类型信息
object obj = Activator.CreateInstance(type); // 动态创建实例
Type
对象是反射的基础,提供了关于类型的详细信息。
Type
对象要使用反射,首先需要获取类型的Type
对象。在C#中,可以通过多种方式获取类型信息。
// 方式1:typeof运算符(编译时已知类型)
Type type1 = typeof(StringBuilder);
// 方式2:GetType()(运行时对象实例)
object obj = new List<int>();
Type type2 = obj.GetType();
// 方式3:Type.GetType()(通过类型名称)
Type type3 = Type.GetType("System.String");
关于获取Type对象的方式,详细信息可见:C# 获取Type对象的方式。
namespace ReflectionDemo
{
public class User { }
internal class Program
{
static void Main(string[] args)
{
var type = typeof(User);
Console.WriteLine($"{"Name".PadRight(24)}:{type.Name}");
Console.WriteLine($"{"FullName".PadRight(24)}:{type.FullName}");
Console.WriteLine($"{"Namespace".PadRight(24)}:{type.Namespace}");
Console.WriteLine($"{"AssemblyQualifiedName".PadRight(24)}:{type.AssemblyQualifiedName}");
}
}
}
运行结果:
Name :User
FullName :ReflectionDemo.User
Namespace :ReflectionDemo
AssemblyQualifiedName :ReflectionDemo.User, ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
关于以上属性,详情内容可见:C# Type类中Name、FullName、Namespace、AssemblyQualifiedName的区别。
Assembly
属性通过Type对象的Assembly
属性 获取 类型所在的程序集。
using System.Reflection;
namespace ReflectionDemo
{
public class User { }
internal class Program
{
static void Main(string[] args)
{
var type = typeof(User);
Assembly assembly = type.Assembly;
Console.WriteLine($"Assembly FullName:{assembly.FullName}");
Console.WriteLine($"Assembly Name:{type.Name}");
}
}
}
输出结果:
Assembly FullName:ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Assembly Name:User
在此我们可知:
【类型的程序集限定名】= 【类型的完全限定名FullName】+【程序集的完全限定名FullName】
当我们通过Type对象的Assembly
属性 获取 类型所在的程序集之后,我们可以对程序集进行相关的操作,具体关于Assembly
的相关内容将在下文进行详细介绍。
namespace ReflectionDemo
{
public class User { }
internal class Program
{
static void Main(string[] args)
{
var type = typeof(User);
Console.WriteLine("基类:" + type.BaseType); // 输出:System.Object
Console.WriteLine($"IsAbstract:{type.IsAbstract}"); // 是否是抽象
Console.WriteLine($"IsAbstract:{type.IsInterface}"); // 是否是接口
Console.WriteLine($"IsAbstract:{type.IsClass}"); // 是否是类
Console.WriteLine($"IsAbstract:{type.IsEnum}"); // 是否是枚举类型
Console.WriteLine($"IsAbstract:{type.IsGenericType}"); // 是否是泛型
Console.WriteLine($"IsAbstract:{type.IsPublic}"); // 是否Public
Console.WriteLine($"IsAbstract:{type.IsSealed}"); // 是否Sealed
Console.WriteLine($"IsAbstract:{type.IsValueType}"); // 是否值类型
}
}
}
反射不仅可以获取类型信息,还可以获取类型的成员信息,如字段、属性、方法等。
class Person
{
public string Name { get; set; }
private int age;
}
class Program
{
static void Main()
{
Type personType = typeof(Person);
FieldInfo[] fields = personType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
Console.WriteLine($"Field: {field.Name}, Type: {field.FieldType}");
}
}
}
class Program
{
static void Main()
{
Type personType = typeof(Person);
PropertyInfo[] properties = personType.GetProperties();
foreach (var property in properties)
{
Console.WriteLine($"Property: {property.Name}, Type: {property.PropertyType}");
}
}
}
class Calculator
{
public int Add(int a, int b) => a + b;
private int Subtract(int a, int b) => a - b;
}
class Program
{
static void Main()
{
Type calculatorType = typeof(Calculator);
MethodInfo[] methods = calculatorType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var method in methods)
{
Console.WriteLine($"Method: {method.Name}, Return Type: {method.ReturnType}");
}
}
}
Type
类 的详情介绍,可见:C# Type 类使用详解。BindingFlags
的详细内容,可见:C# BindingFlags 使用详解反射不仅限于获取类型和成员信息,还可以用于动态创建对象。
以Person
类 为例:
class Person
{
public int Age { get; set; }
public string Name { get; set; }
public Person()
{
Console.WriteLine("无参构造函数被执行!");
}
public Person(string name)
{
Name = name;
Console.WriteLine($"有参构造函数被执行,Name = {Name}");
}
public Person(string name, int age)
{
Name = name;
Age = age;
Console.WriteLine($"有参构造函数被执行,Name = {Name}、Age = {Age}");
}
public void Show()
{
Console.WriteLine("Person");
}
}
Activator.CreateInstance
创建对象internal class Program
{
static void Main(string[] args)
{
Type personType = typeof(Person);
object personInstance = Activator.CreateInstance(personType);
var person = (Person)personInstance;
person.Show();
}
}
运行结果:
无参构造函数被执行!
Person
internal class Program
{
static void Main(string[] args)
{
Type personType = typeof(Person);
object[] parameters = { "Bob" };
object personInstance = Activator.CreateInstance(personType, parameters);
var person = personInstance as Person;
person?.Show();
}
}
运行结果:
有参构造函数被执行,Name = Bob
Person
internal class Program
{
static void Main(string[] args)
{
Type personType = typeof(Person);
object[] parameters = { "Bob", 12 };
object personInstance = Activator.CreateInstance(personType, parameters);
var person = (Person)personInstance;
person.Show();
}
}
运行结果:
有参构造函数被执行,Name = Bob、Age = 12
Person
Type openType = typeof(List<>);
Type closedType = openType.MakeGenericType(typeof(int));
object list = Activator.CreateInstance(closedType);
Type openType = typeof(Dictionary<,>);
Type closedType = openType.MakeGenericType(typeof(int), typeof(string));
object dict = Activator.CreateInstance(closedType);
Activator.CreateInstance
创建具有参数化构造函数对象实例的时候,需要保持传入的参数一致public Person(string name)
构造函数,需要的参数是 object[] parameters = { "Bob" };
public Person(string name, int age)
构造函数,需要的参数是 object[] parameters = { "Bob", 12 };
Assembly
中的CreateInstance
创建对象internal class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
object personInstance = assembly.CreateInstance("ReflectionDemo.Person");
var person = (Person)personInstance;
person.Show();
}
}
运行结果:
无参构造函数被执行!
Person
internal class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
// 传递参数调用构造函数
object personInstance = assembly.CreateInstance(
"ReflectionDemo.Person",
ignoreCase: false,
bindingAttr: BindingFlags.Public | BindingFlags.Instance, // 指定公共实例构造函数
binder: null,//默认null 即可
args: new object[] { "Bob", 12 },
culture: null,//默认null 即可
activationAttributes: null//默认null 即可
)!;
var person = (Person)personInstance;
person.Show();
}
}
运行结果:
有参构造函数被执行,Name = Bob、Age = 12
Person
args
数组的类型和顺序必须与目标构造函数的参数完全匹配。Assembly.CreateInstance
相对低效,建议优先使用 Activator.CreateInstance
或工厂模式。Assembly.CreateInstance
最终调用 Activator.CreateInstance
,因此两者功能相似,但 Activator 更直接。internal class Program
{
static void Main(string[] args)
{
Type personType = typeof(Person);
// 配置构造函数参数列表
var types = Type.EmptyTypes; //new Type[0];
ConstructorInfo constructorInfo = personType.GetConstructor(types);
// 使用Invoke 执行构造函数,并传入对应的参数 数组
object personInstance = constructorInfo.Invoke(null);//(new object[0]);
var person = (Person)personInstance;
person.Show();
}
}
运行结果:
无参构造函数被执行!
Person
internal class Program
{
static void Main(string[] args)
{
Type personType = typeof(Person);
// 配置构造函数参数列表
var types = new Type[] { typeof(string), typeof(int) };
ConstructorInfo constructorInfo = personType.GetConstructor(types);
// 使用Invoke 执行构造函数,并传入对应的参数 数组
object personInstance = constructorInfo.Invoke(new object[] { "Bob", 12 });
var person = (Person)personInstance;
person.Show();
}
}
运行结果:
有参构造函数被执行,Name = Bob、Age = 12
Person
new Type[] { typeof(string), typeof(int) };
和 new object[] { "Bob", 12 }
保持一致反射不仅限于获取类型和成员信息,还可以用于动态调用方法和访问字段或属性。
使用Type
对象的GetMethod
方法获取MethodInfo
对象,然后调用Invoke
方法执行方法。
using System.Reflection;
namespace ReflectionDemo
{
class Calculator
{
public int Add(int a, int b) => a + b;
public void Show() => Console.WriteLine("Calculator");
}
internal class Program
{
static void Main(string[] args)
{
Type calculatorType = typeof(Calculator);
object calculatorInstance = Activator.CreateInstance(calculatorType);
// 获取指定名称的方法信息
MethodInfo showMethod = calculatorType.GetMethod("Show");
MethodInfo addMethod = calculatorType.GetMethod("Add");
// 执行无参方法
showMethod.Invoke(calculatorInstance, null);
// 执行带参方法
int result = (int)addMethod.Invoke(calculatorInstance, new object[] { 5, 3 });
Console.WriteLine($"Result of Add(5, 3): {result}");
}
}
}
运行结果:
Calculator
Result of Add(5, 3): 8
使用Type
对象的GetField
方法获取FieldInfo
对象,然后使用GetValue
和SetValue
方法访问或设置属性值。
using System;
using System.Reflection;
class Person
{
public string Name { get; set; }
private int age;
}
class Program
{
static void Main()
{
Type personType = typeof(Person);
object personInstance = Activator.CreateInstance(personType);
// 获取 FieldInfo 对象:私有字段
FieldInfo ageField = personType.GetField("age", BindingFlags.NonPublic | BindingFlags.Instance);
// 使用 SetValue 设置字段值
ageField.SetValue(personInstance, 30);
// 使用 GetValue 获取字段值
int ageValue = (int)ageField.GetValue(personInstance);
Console.WriteLine($"Person's Age: {ageValue}");
}
}
使用Type
对象的GetProperty
方法获取PropertyInfo
对象,然后使用GetValue
和SetValue
方法访问或设置属性值。
using System;
using System.Reflection;
class Person
{
public string Name { get; set; }
}
class Program
{
static void Main()
{
Type personType = typeof(Person);
object personInstance = Activator.CreateInstance(personType);
// 获取 PropertyInfo 对象
PropertyInfo nameProperty = personType.GetProperty("Name");
// 使用 SetValue 设置属性值
nameProperty.SetValue(personInstance, "Bob");
// 使用 GetValue 获取属性值
string nameValue = (string)nameProperty.GetValue(personInstance);
Console.WriteLine($"Person's Name: {nameValue}");
}
}
反射可解析程序集的元数据,例如:
using System.Reflection;
namespace ReflectionDemo
{
internal class Program
{
static void Main(string[] args)
{
// 获取当前程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();
// 获取当前程序集完全限定名称
Console.WriteLine("程序集名称:" + currentAssembly.FullName);
// 输出:程序集名称:ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Console.WriteLine("程序集名称:" + currentAssembly.GetName().FullName);
// 输出:程序集名称:ReflectionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Console.WriteLine("程序集名称:" + currentAssembly.GetName().Name);
// 输出:程序集名称:ReflectionDemo
Console.WriteLine("程序集版本:" + currentAssembly.GetName().Version);
// 输出:程序集版本:1.0.0.0
}
}
}
从程序集中获取特定的类型(类、结构等)信息。
// 获取当前程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();
// 获取程序集中的所有类型
Type[] types = currentAssembly.GetTypes();
// 获取指定名称的类型
Type myType = currentAssembly.GetType("Namespace.ClassName");
Assembly.Load
加载// 通过程序集全名加载
string assemblyName = "MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
Assembly assembly = Assembly.Load(assemblyName);
// 加载后自动加载依赖项(如 MyDependency.dll)
Assembly.LoadFrom
加载// 通过路径加载,并自动加载依赖项
string path = @"C:\MyAssembly.dll";
Assembly assembly = Assembly.LoadFrom(path);
// 如果 MyAssembly.dll 依赖 MyDependency.dll,会自动加载
Assembly.LoadFile
加载// 通过路径加载,但不加载依赖项
string path = @"C:\MyAssembly.dll";
Assembly assembly = Assembly.LoadFile(path);
// 手动加载依赖项(如 MyDependency.dll)
Assembly.LoadFile(@"C:\MyDependency.dll");
程序集加载的三种方式,可以在项目中添加该程序集的引用后使用,也可在未添加该程序集的情况下使用(某些情况下),这样就极大的丰富的项目的灵活性和扩展性
// 加载 DLL
Assembly pluginAssembly = Assembly.LoadFrom("Plugin.dll");
// 获取类型
Type pluginType = pluginAssembly.GetType("Plugin.MainClass");
// 创建实例
object plugin = Activator.CreateInstance(pluginType);
// 调用插件方法
MethodInfo executeMethod = pluginType.GetMethod("Execute");
executeMethod.Invoke(plugin, null);
通过 BindingFlags 组合实现私有成员的访问:
// 访问私有字段
FieldInfo privateField = type.GetField("_privateField",
BindingFlags.NonPublic | BindingFlags.Instance);
privateField.SetValue(instance, "secret");
// 调用私有方法
MethodInfo privateMethod = type.GetMethod("InternalProcess",
BindingFlags.NonPublic | BindingFlags.Instance);
privateMethod.Invoke(instance, null);
使用MakeGenericMethod
动态创建泛型方法实例
using System.Reflection;
using System.Xml.Linq;
namespace ReflectionDemo
{
public class GenericHelper
{
// 定义一个泛型方法,接受类型参数 T,并打印类型名称
public static void PrintGenericType<T>()
{
Console.WriteLine($"泛型类型参数 T 的类型是:{typeof(T).Name}");
}
}
internal class Program
{
static void Main(string[] args)
{
// 1. 获取目标类的 Type 对象
Type targetType = typeof(GenericHelper);
// 2. 通过反射获取泛型方法的 MethodInfo(注意方法名和参数列表)
// 这里方法 PrintGenericType 没有参数,所以参数类型数组为空
MethodInfo genericMethod = targetType.GetMethod("PrintGenericType");
if (genericMethod == null)
{
Console.WriteLine("未找到泛型方法!");
return;
}
// 3. 使用 MakeGenericMethod 指定类型参数(例如 int 和 string)
// 创建一个类型参数数组,这里指定 T 为 int
Type[] typeArgs = { typeof(int) };
MethodInfo closedMethod = genericMethod.MakeGenericMethod(typeArgs);
// 4. 调用闭合后的泛型方法
closedMethod.Invoke(null, null); // 静态方法无需实例,参数数组为空
// 再次调用,指定类型参数为 string
Type[] typeArgs2 = { typeof(string) };
MethodInfo closedMethod2 = genericMethod.MakeGenericMethod(typeArgs2);
closedMethod2.Invoke(null, null);
}
}
}
public class GenericHelper
{
public static void PrintTwoTypes<T1, T2>()
{
Console.WriteLine($"T1: {typeof(T1).Name}, T2: {typeof(T2).Name}");
}
}
// 通过反射调用:
MethodInfo genericMethod = targetType.GetMethod("PrintTwoTypes");
MethodInfo closedMethod = genericMethod.MakeGenericMethod(typeof(int), typeof(string));
closedMethod.Invoke(null, null); // 输出:T1: Int32, T2: String
为了更直观地理解反射的使用,下面是一个完整的代码示例。
using System;
using System.Reflection;
// 定义Person类
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public void Introduce()
{
Console.WriteLine($"My name is {Name}, and I am {Age} years old.");
}
public string GetGreeting()
{
return $"Hello, my name is {Name}.";
}
}
// 反射示例
public class ReflectionExample
{
public static void Main()
{
// 获取Person类型的Type对象
Type personType = typeof(Person);
// 使用Activator.CreateInstance方法创建Person对象
object person = Activator.CreateInstance(personType, "Alice", 30);
Person alice = (Person)person;
// 调用Introduce方法
alice.Introduce();
// 使用反射调用GetGreeting方法
MethodInfo greetingMethod = personType.GetMethod("GetGreeting");
string greeting = (string)greetingMethod.Invoke(alice, null);
Console.WriteLine(greeting);
// 使用反射访问和设置Name属性
PropertyInfo nameProperty = personType.GetProperty("Name");
string name = (string)nameProperty.GetValue(alice);
Console.WriteLine($"Name: {name}");
nameProperty.SetValue(alice, "Bob");
name = (string)nameProperty.GetValue(alice);
Console.WriteLine($"Updated Name: {name}");
}
}
在这个示例中,我们通过反射获取了 Person
类的类型信息,动态创建了对象实例,并设置了字段和属性的值,最后调用了方法。
虽然反射提供了极大的灵活性,但其性能开销相对较高(反射涉及动态类型解析,比直接调用慢10-100倍。)。频繁使用反射可能会影响应用程序的性能,特别是在需要高效率的场景下。为了优化性能,可以考虑以下几点:
Type
对象进行缓存MethodInfo
、PropertyInfo
并缓存。System.Linq.Expressions
生成动态方法操作类型 | 直接调用 | 反射调用 | 委托缓存 | Emit IL |
---|---|---|---|---|
简单方法调用(100万次) | 5ms | 6500ms | 15ms | 8ms |
属性访问(100万次) | 2ms | 3200ms | 10ms | 6ms |
以上数据,仅供参考。
缓存反射结果可以显著提高应用程序的性能,尤其是在频繁使用反射获取类型信息、方法调用等场景下。下面我将给出一个简单的C#示例,展示如何缓存反射的结果。
using System;
using System.Collections.Generic;
using System.Reflection;
public class ReflectionCacheExample
{
private static Dictionary<string, MethodInfo> _methodInfoCache = new Dictionary<string, MethodInfo>();
public void ExecuteMethod(string methodName)
{
var methodInfo = GetMethodInfo(methodName);
if (methodInfo != null)
{
// 调用方法
methodInfo.Invoke(this, null);
}
else
{
Console.WriteLine($"未找到名为 {methodName} 的方法");
}
}
private MethodInfo GetMethodInfo(string methodName)
{
string key = $"{this.GetType().FullName}.{methodName}";
// 检查缓存中是否已有该方法的信息
if (!_methodInfoCache.TryGetValue(key, out MethodInfo cachedMethod))
{
// 如果缓存中没有,则通过反射查找并添加到缓存
MethodInfo method = this.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (method != null)
{
_methodInfoCache[key] = method;
return method;
}
return null;
}
return cachedMethod;
}
// 示例方法
private void PrintHello()
{
Console.WriteLine("Hello, World!");
}
}
class Program
{
static void Main(string[] args)
{
ReflectionCacheExample example = new ReflectionCacheExample();
example.ExecuteMethod("PrintHello"); // 第一次调用,会进行反射查找
example.ExecuteMethod("PrintHello"); // 第二次调用,直接从缓存读取
}
}
代码说明
ReflectionCacheExample
类,它包含一个用于缓存方法信息的静态字典_methodInfoCache
。GetMethodInfo
方法首先尝试从缓存中检索方法信息。如果找不到,则通过反射获取方法信息,并将其存储在缓存中以便将来使用。ExecuteMethod
方法演示了如何利用缓存来执行方法。第一次调用时,由于缓存为空,所以需要通过反射查找方法;第二次调用时,直接从缓存中获取方法信息,提高了效率。缓存属性信息
private static Dictionary<Type, PropertyInfo[]> _propertyCache = new();
public static PropertyInfo[] GetCachedProperties(Type type)
{
if (!_propertyCache.TryGetValue(type, out var props))
{
props = type.GetProperties();
_propertyCache[type] = props;
}
return props;
}
下面是一个完整的C#示例,展示了如何使用表达式树创建一个属性访问器(getter),并将其应用于具体的类实例中。我们将以Person
类为例,并展示如何获取其Name
属性的值。
using System;
using System.Linq.Expressions;
using System.Reflection;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
public static class PropertyGetterFactory
{
public static Func<object, object> CreatePropertyGetter(PropertyInfo prop)
{
var objParam = Expression.Parameter(typeof(object), "obj");
var castExpr = Expression.Convert(objParam, prop.DeclaringType);
var propAccess = Expression.Property(castExpr, prop);
var castResult = Expression.Convert(propAccess, typeof(object));
return Expression.Lambda<Func<object, object>>(castResult, objParam).Compile();
}
}
class Program
{
static void Main(string[] args)
{
// 创建一个Person对象
var personObj = new Person("Alice", 30);
// 获取"Name"属性的PropertyInfo
var propertyInfo = typeof(Person).GetProperty("Name");
if (propertyInfo == null)
{
Console.WriteLine("未找到指定的属性");
return;
}
// 创建属性访问器
var getter = PropertyGetterFactory.CreatePropertyGetter(propertyInfo);
// 使用属性访问器获取属性值
string name = (string)getter(personObj);
// 输出结果
Console.WriteLine($"Name: {name}");
}
}
代码说明
Person 类:
Name
和 Age
。PropertyGetterFactory 类:
CreatePropertyGetter
方法接收一个 PropertyInfo
对象作为参数。Func
)。Main 方法:
Person
实例并初始化其属性。Person
类的 Name
属性的 PropertyInfo
。CreatePropertyGetter
方法生成属性访问器。personObj
的 Name
属性值,并输出结果。输出结果
Name: Alice
这种方式通过表达式树动态生成属性访问器,可以显著提高反射操作的性能,特别是在需要频繁访问同一属性的情况下。
// 表达式树优化
var param = Expression.Parameter(typeof(MyClass));
var propAccess = Expression.Property(param, "Name");
var lambda = Expression.Lambda<Func<MyClass, string>>(propAccess, param);
Func<MyClass, string> compiled = lambda.Compile();
string value = compiled(instance);
动态方法和IL生成是一种高级技术,通常用于性能优化或在运行时动态生成代码的场景。使用这些技术时需要小心,确保生成的IL代码是正确的并且符合预期的行为。
下面是一个完整的C#示例,展示了如何使用动态方法(DynamicMethod
)和IL生成器(ILGenerator
)来创建一个无参构造函数的委托(ObjectActivator
)。我们将以一个简单的类Person
为例,并展示如何使用这个委托实例化对象。
using System;
using System.Reflection;
using System.Reflection.Emit;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person()
{
Name = "Unknown";
Age = 0;
}
public override string ToString()
{
return $"Name: {Name}, Age: {Age}";
}
}
public delegate object ObjectActivator();
public static class ObjectActivatorFactory
{
public static ObjectActivator CreateParameterlessConstructor(Type type)
{
// 确保类型有一个无参构造函数
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
if (constructor == null)
{
throw new ArgumentException("类型必须包含一个无参构造函数。", nameof(type));
}
// 创建一个新的动态方法
var dynamicMethod = new DynamicMethod(
name: "CreateInstance",
returnType: typeof(object),
parameterTypes: null,
owner: type);
ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Newobj, constructor); // 调用无参构造函数
il.Emit(OpCodes.Ret); // 返回新创建的对象
return (ObjectActivator)dynamicMethod.CreateDelegate(typeof(ObjectActivator));
}
}
class Program
{
static void Main(string[] args)
{
try
{
// 创建一个用于实例化Person对象的委托
ObjectActivator activator = ObjectActivatorFactory.CreateParameterlessConstructor(typeof(Person));
// 使用委托创建Person对象
object personObj = activator();
// 输出结果
Console.WriteLine(personObj.ToString());
}
catch (Exception ex)
{
Console.WriteLine($"发生错误: {ex.Message}");
}
}
}
代码说明
Person 类:
Name
和 Age
。Name
为"Unknown",Age
为0。ToString
方法,方便输出对象信息。ObjectActivatorFactory 类:
CreateParameterlessConstructor
方法接收一个 Type
对象作为参数。DynamicMethod
创建一个新的动态方法,返回类型为 object
,没有参数。ILGenerator
生成中间语言(IL)代码,调用指定类型的无参构造函数并返回新创建的对象。Main 方法:
Person
对象的委托。Person
对象。输出结果
Name: Unknown, Age: 0
using System;
using System.Linq.Expressions;
using System.Reflection.Emit;
public class Program
{
public static void Main()
{
// 动态生成一个方法
DynamicMethod dynamicMethod = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, typeof(Program).Module);
ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
// 调用动态生成的方法
Func<int, int, int> add = (Func<int, int, int>)dynamicMethod.CreateDelegate(typeof(Func<int, int, int>));
Console.WriteLine(add(2, 3)); // 输出: 5
}
}
场景 | 反射方案 | 替代方案 |
---|---|---|
高性能方法调用 | MethodInfo.Invoke | 表达式树编译委托 |
对象创建 | Activator.CreateInstance | 预编译工厂类 |
类型检查 | IsAssignableFrom | 泛型约束/模式匹配 |
元数据分析 | GetCustomAttributes | 源代码生成 |
dynamic
关键字dynamic
关键字:dynamic obj = new Person();
obj.Name = "李四"; // 动态绑定
dynamic
的区别Type
对象显式操作类型成员,灵活性高但性能低。dynamic
internal class Program
{
static void Main(string[] args)
{
Type type = typeof(User);
object o_user = Activator.CreateInstance(type);
//o_user.Show()
//不可能通过o_class1 调用Show
dynamic d_user = Activator.CreateInstance(type);
d_user.Show("sss");
//可以通过d_user 调用方法Show
//其实o_user 和 d_user得到结果都是一样的,
// 但是因为 object 时编译时类型,object本身没有Show方法,因此调用会报错
// 而dynamic 是运行时类型,编译状态下会绕过编译器的检查,直到真正运行后才确定其数据类型
Console.ReadLine();
}
}
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。