我想构建一个动态代理对象,为对象添加某些功能。
基本上,我想接收一个对象,用一个看起来与我得到的原始对象相同的对象包装它,并拦截所有调用。
class Wrapper : DynamicProxy// dynamic proxy is not a reall class, but i guess something like this exists...
{
public static T Wrap(T obj)
{
return (T) new Wrapper(obj);
}
public override object InterceptCall(MethodInfo info, object[] args)
{
// do stuff
}
}
只是为了澄清,我想做一些类似于WCF通道工厂…
我添加了一个赏金,因为我需要一个好方法来代理类(不是接口)和处理非虚拟方法(就好像我继承并添加了一个方法在"new"关键字下)。我相信这一切都是非常可能的,因为。net做到了。
你可以使用DynamicObject和ImpromptuInterface的组合来实现这一点,但是你必须有一个接口来实现你想要代理的函数和属性。
public interface IDoStuff
{
void Foo();
}
public class Wrapper<T> : DynamicObject
{
private readonly T _wrappedObject;
public static T1 Wrap<T1>(T obj) where T1 : class
{
if (!typeof(T1).IsInterface)
throw new ArgumentException("T1 must be an Interface");
return new Wrapper<T>(obj).ActLike<T1>();
}
//you can make the contructor private so you are forced to use the Wrap method.
private Wrapper(T obj)
{
_wrappedObject = obj;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
try
{
//do stuff here
//call _wrappedObject object
result = _wrappedObject.GetType().GetMethod(binder.Name).Invoke(_wrappedObject, args);
return true;
}
catch
{
result = null;
return false;
}
}
}
当然,您可以选择放弃类型安全,使用我所展示的DynamicObject
,然后放弃duck-casting。我为这个对象代理做了一个透明的可扩展版本,并在这里开源了它。
除了Castle。动态代理,也有林甫。
我应该早点写的,不过没关系。
我的问题有一个特殊的"陷阱",我需要能够代理类而不是接口。
有两个解决方案:
-
RealProxy和朋友,基本上意味着使用。net Remoting。需要从ContextBoundObject继承。
- 这种方法利用了。net JIT编译器提供的"魔力"(这是硬编码的,专门识别
RealProxy
),让您"覆盖"非虚拟成员。 -
使用System.Reflection.Emit构建代理,正如spring所做的那样,你也可以看看它们的ProxyFactoryObject的代码。这里是关于这个主题的另外三篇文章。
这种方法有一个关键的缺点,那就是你只能覆盖
virtual
成员。 。. NET 6.0为Reflection
名称空间添加了一个新的候选名称空间:DispatchProxy。团队在这里宣布。本文中包含一个示例用法。
看看PostSharp。我不知道有什么方法可以在。net中实现你想要的,但是PostSharp提供了像OnMethodBoundaryAspect"可用于替换或包装方法中的代码。
我用它来做日志,参数验证,异常处理等。
有一个免费的社区版,它应该为您工作。您需要将它安装在您的开发机器上,以及您使用的任何构建服务器上。
另一个选项是ContextBoundObject
。
大约8-9年前,CodeProject上有一篇文章使用这种方法跟踪方法调用。
对于在类中的每个函数之前和之后添加任何功能,Real proxy是一种很好的方法。
所以现在在T中可以是任何TestClass。为TestClass创建实例
var _instance =(对象)了DynamicProxy (TestClass) .GetTransparentProxy ();
动态代理的代码-
class DynamicProxy<T> : RealProxy
{
readonly T decorated;
public DynamicProxy(T decorated) : base(typeof(T))
{
this.decorated = decorated;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
string fullMethodName = $"{methodInfo.DeclaringType.Name}.{methodCall.MethodName}";
try
{
var result = methodInfo.Invoke(decorated, methodCall.InArgs);
return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
return new ReturnMessage(e, methodCall);
}
finally
{
}
}
}
你不能拦截对静态成员的所有调用,而不是对虚拟成员或私有成员的调用,除非你让CLR钩子到对该对象的每个方法/属性调用中,并将调用重定向到你创建的对象。你可以通过使用。net Profiler API来实现。例如,TypeMock隔离器使用它监视应用程序的执行,当方法被调用时,CLR通知TypeMock隔离器,这允许隔离器完全覆盖原始类。
您可以使用来自System的DynamicObject来完成此操作。命名空间,不使用任何第三方库。
示例代码:
public class DynamicProxy: DynamicObject
{
private readonly T _object;
// The inner dictionary.
Dictionary<string, object> dictionary = new Dictionary<string, object>();
// Getting a property.
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return dictionary.TryGetValue(binder.Name, out result);
}
// Setting a property.
// You can set up access control eg. if you don't want to
// set certain field, you can return false before putting
// the value into the inner dictionary
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (binder.Name.Equals("Rating")) return false;
dictionary[binder.Name] = value;
return true;
}
public DynamicProxy(T object)
{
_object = object;
dictionary["Name"] = object.GetName();
dictionary["Gender"] = object.GetGender();
dictionary["Interests"] = object.GetInterests();
dictionary["Rating"] = object.GetGeekRating();
}
public string GetName()
{
return (string)dictionary["Name"];
}
public int GetGeekRating()
{
return (int)dictionary["Rating"];
}
}
然后在驱动类上:
dynamic dynamicProxy = new DynamicProxy(person);
这样,您就可以设置和获取具有访问控制的字段。