动态接口实现






4.93/5 (22投票s)
动态生成实现接口的代理类
引言
本文展示了如何在运行时生成实现给定接口的代理类。在我们的解决方案中,运行时生成的实现器类派生自一个基类,并且通过实现接口的所有调用都将委托给它。基类包含用于属性获取、属性设置、方法调用、事件订阅/取消订阅的通用处理方法。我们的目的是在运行时生成一个实现给定接口的类,该类继承自此基类,并以适当的参数调用基类的相应方法。
接口的代理生成在以下情况下可能很有用:每个方法调用/属性访问/事件处理都具有相同的(代理)实现。例如,如果某个接口没有可用的具体实现,则可以将其替换为代理实现。此代理可以记录和排队所有调用,并在具体实现可用时进行处理。
另一个有用的情况是,当您从未拥有接口的实现时,因为它在不同的位置实现。在这种情况下,您可以创建一个代理,将每次调用及其参数传输到远程位置。
背景
我们使用 System.Reflection.Emit
构建器类来生成动态程序集、类和 IL 代码。abstract
基类函数名称遵循 DynamicObject
类的命名约定。
Using the Code
您可以在图 1 中看到主要的接口和类。
动态生成的实现器类的 abstract
基类称为 DynamicProxy
。它有四个必须在具体类中实现的 abstract
方法。当请求属性值时,将调用 TryGetMember
方法;类似地,当设置属性值时,将调用 TrySetMember
方法。每当通过已实现的接口调用方法时,将调用 TryInvokeMember
。最后,当发生事件订阅或取消订阅时,将调用 TrySetEvent
方法。(与 DynamicObject
类似,我们对属性设置和事件设置使用相同的 protected
方法,称为 TrySetMemberInternal
。在此方法中,属性设置和事件订阅是分开的,并调用相应的 abstract
方法。)
public abstract class DynamicProxy : IDisposable
{
....
protected DynamicProxy()
{
}
protected abstract bool TryInvokeMember(Type interfaceType, string name, object[] args, out object result);
protected abstract bool TrySetMember(Type interfaceType, string name, object value);
protected abstract bool TryGetMember(Type interfaceType, string name, out object result);
protected abstract bool TrySetEvent(Type interfaceType, string name, object value);
protected bool TrySetMemberInternal(Type interfaceType, string name, object value)
{
bool ret;
if (TypeHelper.HasEvent(interfaceType, name))
{
ret = TrySetEvent(interfaceType, name, value);
}
else
{
ret = TrySetMember(interfaceType, name, value);
}
return ret;
}
protected virtual void Dispose(bool disposing)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~DynamicProxy()
{
Dispose(false);
}
}
动态接口实现
IDynamicInterfaceImplementor
接口定义了创建运行时生成的代理类型的契约。它只有一个方法 CreateType
,它接受两个 Type 参数,第一个表示要实现的接口,另一个表示基类的类型。该方法返回一个符合上述要求的新 Type
。
public interface IDynamicInterfaceImplementor
{
/// <summary>
/// Create a type which implements the given interface and inherits from the given base type.
/// Every call can be handled in the base class proper method.
/// </summary>
/// <param name="interfaceType">Interface to implement</param>
/// <param name="dynamicProxyBaseType">Base class, which should inherit from <see cref="DynamicProxy"/></param>
/// <returns>The new type</returns>
Type CreateType(Type interfaceType, Type dynamicProxyBaseType);
}
DynamicInterfaceImplementor
类实现了这个接口,并为我们完成了繁重的工作。为了创建新类型,它使用了 System.Reflection.Emit
命名空间中的构建器类。首先,借助 DefineDynamicAssembly
方法,从当前 AppDomain
请求一个 AssemblyBuilder
。新生成的程序集名称中包含一个随机部分,因此可以同时使用多个接口实现器。每个程序集至少需要一个模块,因此在下一步中,会将一个动态模块添加到程序集。AssemblyBuilder
有一个 DefineDynamicModule
方法,它创建一个模块并返回对其 ModuleBuilder
的引用。
private ModuleBuilder moduleBuilder = null;
private void Init()
{
...
Type ownClass = typeof(DynamicInterfaceImplementor);
string guid = Guid.NewGuid().ToString();
AssemblyName assemblyName = new AssemblyName(string.Concat(ownClass.Namespace, ".", ownClass.Name, "_", guid));
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
moduleBuilder = ab.DefineDynamicModule(assemblyName.Name, string.Concat(assemblyName.Name, ".dll"));
...
}
模块是程序集中的一个逻辑容器,它包含所有类,因此 ModuleBuilder
有一个 DefineType
方法,它返回一个 TypeBuilder
,我们可以用它来构造一个新类型。它有许多我们可以用来提供类型细节的直接方法。例如,SetParent
和 AddInterfaceImplementation
帮助我们为新生成的类指定基类和实现的接口。dynamicProxyBaseType
被设置为基类。interfaceType
被作为实现的接口添加到类型声明中。
public Type CreateType(Type interfaceType, Type dynamicProxyBaseType)
{
...
string typeName = string.Concat(ownClassName, "+", interfaceType.FullName);
TypeBuilder tb = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
tb.SetParent(dynamicProxyBaseType);
tb.AddInterfaceImplementation(interfaceType);
CreateConstructorBaseCalls(dynamicProxyBaseType, tb);
DynamicImplementInterface(new List<Type> { interfaceType }, new List<string>(), interfaceType, tb);
ret = tb.CreateType();
...
}
现在,我们生成并添加到类型中接口成员的实现。这发生在 DynamicImplementImplementation
方法中,该方法遍历给定接口的所有成员并实现它们。它还会搜索基接口,如果找到,则递归地实现它们。我们将已实现的成员名称存储在 usedNames
变量中,因为目前不支持接口层次结构中相同的成员名称。
private void DynamicImplementInterface(List<Type> implementedInterfaceList, List<string> usedNames, Type interfaceType, TypeBuilder tb)
{
List<MethodInfo> propAccessorList = new List<MethodInfo>();
GenerateProperties(usedNames, interfaceType, tb, propAccessorList);
GenerateEvents(usedNames, interfaceType, tb, propAccessorList);
GenerateMethods(usedNames, interfaceType, tb, propAccessorList);
foreach (Type i in interfaceType.GetInterfaces())
{
if (!implementedInterfaceList.Contains(i))
{
DynamicImplementInterface(implementedInterfaceList, usedNames, i, tb);
implementedInterfaceList.Add(i);
}
}
}
属性生成
首先,我们遍历接口定义中的属性。我们的目的是将属性 getter 的每次调用转发到基类的 TryGetMember
方法。基类负责返回正确的值。为了定义新属性,我们使用 DefineProperty
方法。此属性需要一个用作 getter 方法并包含实现细节的方法。此方法必须设置为属性的 getter 方法。您可以使用 PropertyBuilder.
SetGetMethod
函数来完成。这个新的 getter 方法是借助 TypeBuilder.
DefineMethod
定义的。这会返回一个 MethodBuilder
,它有一个 GetILGenerator
方法,该方法返回一个 ILGenerator
,它可以发出 IL 操作码。有了这个类,我们可以用 IL 代码编写方法的实现细节。在我们的具体实现中,它包含对基类的 TryGetMember
方法的调用。EmitPropertyGet
中的 IL 代码实现不属于本文。
private void GenerateProperties(List<string> usedNames, Type interfaceType, TypeBuilder tb, List<MethodInfo> propAccessors)
{
foreach (PropertyInfo propertyInfo in interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
...
PropertyBuilder pb = tb.DefineProperty(propertyInfo.Name, propertyInfo.Attributes, propertyInfo.PropertyType, null);
if (propertyInfo.CanRead)
{
MethodInfo getMethodInfo = propertyInfo.GetGetMethod();
propAccessors.Add(getMethodInfo);
MethodBuilder getMb = tb.DefineMethod(getMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, propertyInfo.PropertyType, Type.EmptyTypes);
ILGenerator ilGenerator = getMb.GetILGenerator();
EmitPropertyGet(ilGenerator, propertyInfo);
pb.SetGetMethod(getMb);
tb.DefineMethodOverride(getMb, getMethodInfo);
}
...
}
}
如果属性有 setter 访问器,那么我们为 setter 方法生成另一个方法。您可以使用 SetSetMethod
函数将此方法设置为属性的 setter 方法。在 EmitPropertySet
中生成的 IL 代码将属性名称和新值作为参数调用基类的 TrySetMemberInternal
方法。
if (propertyInfo.CanWrite)
{
MethodInfo setMethodInfo = propertyInfo.GetSetMethod();
propAccessors.Add(setMethodInfo);
MethodBuilder setMb = tb.DefineMethod(setMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { propertyInfo.PropertyType });
ILGenerator ilGenerator = setMb.GetILGenerator();
EmitPropertySet(ilGenerator, propertyInfo);
pb.SetSetMethod(setMb);
tb.DefineMethodOverride(setMb, setMethodInfo);
}
事件生成
然后我们遍历所有事件。新的事件在代理类中借助 DefineEvent
方法生成。我们还需要一个字段来存储包含事件调用列表的委托。生成的事件的 add
和 remove
访问器必须定义为方法。这两个方法都调用基类中的 TrySetMemberInternal
方法。调用有两个参数,第一个包含事件的名称,第二个包含新的 Delegate
。在调用 TrySetMemberInternal
方法之前,add
函数调用 Delegate.Combine
以将旧的调用列表与新的委托连接起来。类似地,remove
函数调用 Delegate.Remove
以从调用列表中删除委托。此行为通过 EmitEventAdd
和 EmitEventRemove
方法中的 IL 代码实现,本文未详细介绍。
private void GenerateEvents(List<string> usedNames, Type interfaceType, TypeBuilder tb, List<MethodInfo> propAccessors)
{
foreach (EventInfo eventInfo in interfaceType.GetEvents(BindingFlags.Instance | BindingFlags.Public))
{
...
EventBuilder eb = tb.DefineEvent(eventInfo.Name, eventInfo.Attributes, eventInfo.EventHandlerType);
FieldBuilder ef = tb.DefineField(string.Concat("_", eventInfo.Name),
eventInfo.EventHandlerType, FieldAttributes.Private);
//add
{
MethodInfo addMethodInfo = eventInfo.GetAddMethod();
propAccessors.Add(addMethodInfo);
MethodBuilder getMb = tb.DefineMethod(addMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { eventInfo.EventHandlerType });
ILGenerator ilGenerator = getMb.GetILGenerator();
EmitEventAdd(ilGenerator, eventInfo, ef);
tb.DefineMethodOverride(getMb, addMethodInfo);
}
//remove
{
MethodInfo removeMethodInfo = eventInfo.GetRemoveMethod();
propAccessors.Add(removeMethodInfo);
MethodBuilder getMb = tb.DefineMethod(removeMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { eventInfo.EventHandlerType });
ILGenerator ilGenerator = getMb.GetILGenerator();
EmitEventRemove(ilGenerator, eventInfo, ef);
tb.DefineMethodOverride(getMb, removeMethodInfo);
}
}
}
方法生成
最后,我们为新类生成方法。遍历接口的所有方法,我们为每个方法生成一个实现。该实现包含对基类的 TryInvokeMember
方法的调用。它传递方法名和方法的所有参数,并返回 TryInvokeMember
函数的返回值。EmitInvokeMethod
函数生成此方法的 IL 代码,此处未详细描述。
private void GenerateMethods(List<string> usedNames, Type interfaceType, TypeBuilder tb, List<MethodInfo> propAccessors)
{
foreach (MethodInfo mi in interfaceType.GetMethods())
{
...
var parameterInfoArray = mi.GetParameters();
var genericArgumentArray = mi.GetGenericArguments();
if (!propAccessors.Contains(mi))
{
MethodBuilder mb = tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, mi.ReturnType, parameterInfoArray.Select(pi => pi.ParameterType).ToArray());
if (genericArgumentArray.Any())
{
mb.DefineGenericParameters(genericArgumentArray.Select(s => s.Name).ToArray());
}
EmitInvokeMethod(mi, mb);
tb.DefineMethodOverride(mb, mi);
}
}
}
构造函数创建
因此,我们实现了给定接口的所有成员。接下来,我们需要根据基类的构造函数来定义构造函数。例如,如果基类没有默认构造函数,我们必须生成一个直通构造函数。这个构造函数方法获取相同的参数并将它们传递给基类的构造函数。我们根据这篇文章生成直通构造函数。
实例化
CreateType
方法创建新类型并返回对新 Type
类的引用。此 Type
可以用于借助 IDynamicProxyFactory
接口创建新实例。CreateDynamicProxy
方法期望两个参数,第一个是要实现的接口类型,第二个是要传递给构造函数的参数数组,它返回新实例。CreateDynamicProxy 有一个泛型重载,它在泛型参数中获取接口类型,因此它可以返回一个类型化实例。创建新实例的序列图如下所示
工厂接口代码显示了这些方法的签名
public interface IDynamicProxyFactory
{
/// <summary>
/// Creates an instance of a class which implements the given interface
/// </summary>
/// <typeparam name="TInterfaceType">Interface to be implemented</typeparam>
/// <param name="constructorParameters">ctor parameters for creating the new instance</param>
/// <returns>The new instance</returns>
TInterfaceType CreateDynamicProxy<TInterfaceType>(params object[] constructorParameters);
/// <summary>
/// Creates an instance of a class which implements the given interface
/// </summary>
/// <param name="interfaceType">Interface to be implemented</param>
/// <param name="constructorParameters">ctor parameters for creating the new instance</param>
/// <returns>The new instance</returns>
object CreateDynamicProxy(Type interfaceType, params object[] constructorParameters);
}
有一个名为 DynamicProxyFactory
的泛型类实现了这个接口。这个类有一个泛型参数,它定义了所有生成的代理类的基类。在构造函数中,它获取一个 IDynamicInterfaceImplementor
实例以用于代理类生成。创建新类后,该方法借助 Activator
类创建新实例。构造函数参数被传递给 CreateInstance
方法。
public class DynamicProxyFactory<TDynamicProxyType> : IDynamicProxyFactory where TDynamicProxyType : DynamicProxy
{
private IDynamicInterfaceImplementor interfaceImplementor = null;
public DynamicProxyFactory(IDynamicInterfaceImplementor interfaceImplementor)
{
this.interfaceImplementor = interfaceImplementor;
}
public virtual TInterfaceType CreateDynamicProxy<TInterfaceType>(params object[] constructorParameters)
{
TInterfaceType ret;
ret = (TInterfaceType)CreateDynamicProxy(typeof(TInterfaceType), constructorParameters);
return ret;
}
public virtual object CreateDynamicProxy(Type interfaceType, params object[] constructorParameters)
{
if (interfaceType == null)
{
throw new ArgumentNullException("interfaceType");
}
if (!interfaceType.IsInterface)
{
throw new ArgumentException("interfaceType must be an interface!"); //LOCSTR
}
object ret = null;
Type t = interfaceImplementor.CreateType(interfaceType, typeof(TDynamicProxyType));
ret = Activator.CreateInstance(t, constructorParameters);
return ret;
}
}
该图显示了新实例创建和接口调用的调用序列。
关注点
如果您需要具有可定制通用行为的接口的代理类实例,这种架构提供了一个解决方案。在我们的项目中,我们有一个动态代理实现,它在客户端和服务器之间进行远程调用,将调用的所有参数传递给远程端。它帮助我们隐藏服务层。我将在另一篇文章中向您展示这种架构。
历史
- 2014 年 3 月 28 日:初始版本