在前面的文章中介绍了dotNet开发之反射技术的基础篇和动态库的加载,我们将深入探讨反射的其他用法,除了常规的使用方式,反射还可以实现一些高级功能,如动态创建类型、调用私有方法,访问泛型类型,自定义序列化和反序列化,动态代理等。下面介绍一些 C# 反射的高级用法:
动态创建类型
Emit 是 C# 中的一个动态代码生成工具,可以通过 Emit 在运行时动态生成 MSIL 代码,从而实现高效的动态代码生成和执行。在反射中,我们可以使用 Emit 来动态创建类型、方法、属性等元素,并在运行时进行操作。反射可以使用 System.Reflection.Emit 命名空间提供的类型动态创建类。
以下示例代码演示了如何使用反射动态创建一个简单的类型:
using System;
using System.Reflection;
using System.Reflection.Emit;
class Program
{
static void Main(string[] args)
{
// 创建一个动态程序集
AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("DynamicModule", "DynamicAssembly.dll");
// 创建一个动态类型
TypeBuilder dynamicType = dynamicModule.DefineType("DynamicType", TypeAttributes.Public);
// 添加一个字段
FieldBuilder dynamicField = dynamicType.DefineField("dynamicField", typeof(int), FieldAttributes.Private);
// 添加一个方法
MethodBuilder dynamicMethod = dynamicType.DefineMethod("DynamicMethod", MethodAttributes.Public, typeof(void), null);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldc_I4, 42);
ilGenerator.Emit(OpCodes.Stfld, dynamicField);
ilGenerator.Emit(OpCodes.Ret);
// 创建类型
Type type = dynamicType.CreateType();
// 调用方法
object instance = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("DynamicMethod");
methodInfo.Invoke(instance, null);
// 保存程序集
dynamicAssembly.Save("DynamicAssembly.dll");
}
}
在上述示例代码中,我们使用反射动态创建了一个名为 DynamicType 的类型,并向其中添加了一个私有整型字段和一个名为 DynamicMethod 的公共方法。在该方法内部,我们将数字 42 存储到该类型的私有字段中。最后,我们调用 Invoke 方法来执行该方法并输出结果。
调用私有方法
反射可以使用 BindingFlags.NonPublic 标志访问对象的私有成员。以下示例代码演示了如何使用反射调用私有方法:
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
Example obj = new Example();
MethodInfo methodInfo = typeof(Example).GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
methodInfo.Invoke(obj, null);
}
}
class Example
{
private void PrivateMethod()
{
Console.WriteLine("This is a private method.");
}
}
在上述示例代码中,我们定义了一个名为 Example 的类,并向其中添加了一个私有方法 PrivateMethod。然后,我们使用反射获取该方法并调用它。由于该方法是私有方法,因此需要使用 BindingFlags.NonPublic 标志访问它。
访问泛型类型
反射可以使用 MakeGenericType 方法创建泛型类型的实例。以下示例代码演示了如何使用反射访问泛型类型:
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
Type genericType = typeof(MyGenericClass<>).MakeGenericType(typeof(int));
object instance = Activator.CreateInstance(genericType);
MethodInfo methodInfo = genericType.GetMethod("MyMethod");
methodInfo.Invoke(instance, null);
}
}
class MyGenericClass<T>
{
public void MyMethod()
{
Console.WriteLine(#34;Type: {typeof(T)}");
}
}
在上述示例代码中,我们定义了一个泛型类 MyGenericClass<T>,其中包含一个名为 MyMethod 的公共方法。然后,我们使用 typeof(MyGenericClass<>) 获取泛型类型,并使用 MakeGenericType 方法指定类型参数为整型 int。最后,我们创建该泛型类型的实例,并调用 MyMethod 方法,输出结果。
自定义序列化和反序列化
在 C# 中,序列化和反序列化是将对象转换为二进制格式或其他格式以便于存储和传输的重要技术。在反射中,我们可以使用自定义序列化和反序列化来控制对象的序列化和反序列化过程,以实现更高级的功能。
例如,下面的代码演示了如何使用反射和自定义序列化将一个对象序列化为 JSON 格式:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Text;
using Newtonsoft.Json;
class Program
{
static void Main()
{
Person person = new Person { Name = "Alice", Age = 25 };
string json = SerializeToJson(person);
Console.WriteLine(json);
Person deserialized = DeserializeFromJson<Person>(json);
Console.WriteLine(deserialized.Name);
Console.WriteLine(deserialized.Age);
}
static string SerializeToJson(object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (PropertyInfo property in properties)
{
dict[property.Name] = property.GetValue(obj);
}
return JsonConvert.SerializeObject(dict);
}
static T DeserializeFromJson<T>(string json)
{
Type type = typeof(T);
Dictionary<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
object instance = Activator.CreateInstance(type);
foreach (KeyValuePair<string, object> kvp in dict)
{
PropertyInfo property = type.GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance);
if (property != null && property.CanWrite)
{
property.SetValue(instance, Convert.ChangeType(kvp.Value, property.PropertyType));
}
}
return (T)instance;
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
上述代码使用反射和 JsonConvert 类将一个 Person 对象序列化为 JSON 格式,并将其反序列化为一个新的对象。在实际应用中,你可以使用类似的技术实现自定义的序列化和反序列化逻辑,并将对象转换为任何你想要的格式。
动态代理
动态代理是一种在运行时创建代理对象的技术,可以用于实现各种高级功能,例如 AOP(面向切面编程)和远程调用。在反射中,我们可以使用动态代理来动态生成代理对象并在运行时进行操作。
Castle.Core 库使用了反射技术来实现动态代理。动态代理是通过在运行时创建代理对象来实现的,而反射是一种在运行时获取和操作类型、对象、方法等元素的技术。在动态代理的实现中,Castle.Core 使用了反射来生成代理类,并通过拦截器(Interceptor)来截获方法调用,并在调用前后执行额外的逻辑。
具体来说,Castle.Core 使用反射来生成代理类的 IL 代码,然后通过 Emit 将其编译为可执行代码。这些动态生成的代码会在运行时被加载到内存中,并根据需要创建代理对象。
在代理对象被调用时,Castle.Core 使用反射来获取被代理对象的方法信息,并利用 Emit 动态生成 IL 代码,将方法调用委托给拦截器。拦截器可以在调用前后执行自定义的逻辑,例如输出日志、实现事务管理等。通过这种方式,Castle.Core 实现了动态代理的功能。
例如,下面的代码演示了如何使用 Castle.Core 库来创建一个动态代理对象,该对象会在调用方法前后输出日志:
using System;
using System.Reflection;
using Castle.DynamicProxy;
class Program
{
static void Main()
{
ICalculator calculator = CreateProxy<ICalculator>(new Calculator());
int result = calculator.Add(1, 2);
Console.WriteLine(result);
}
static TInterface CreateProxy<TInterface>(object target) where TInterface : class
{
ProxyGenerator generator = new ProxyGenerator();
return generator.CreateInterfaceProxyWithTarget<TInterface>(target, new LoggingInterceptor());
}
}
interface ICalculator
{
int Add(int x, int y);
}
class Calculator : ICalculator
{
public int Add(int x, int y)
{
return x + y;
}
}
class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine(#34;Before calling {invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine(#34;After calling {invocation.Method.Name}");
}
}
上述代码使用 Castle.Core 库中的 ProxyGenerator 类动态生成一个实现 ICalculator 接口的代理对象,并在调用方法前后输出日志。在实际应用中,你可以使用类似的技术来实现各种高级功能,例如性能分析、事务管理等。
总结来说,C# 反射提供了许多强大的功能,可以帮助我们实现各种高级功能和扩展性。在使用反射时,需要注意安全性和性能问题,并遵循良好的编程实践。