[英]How to make a simple dynamic proxy in C#
我想構建一個動態代理 object 來為 object 添加某些功能。
基本上我想收到一個 object,用一個看起來與我得到的原始相同的 object 包裝它,並攔截所有呼叫。
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
,然后放棄鴨子鑄造。
我制作了這個對象代理的透明可擴展版本,並在此處開源。
我應該早點寫這個,但沒關系。
我的問題有一個特殊的“問題”,我需要能夠代理類而不是接口。
對此有兩種解決方案:
RealProxy和朋友們,基本上意味着使用 .NET Remoting。 需要一個從ContextBoundObject繼承。
RealProxy
)提供的“魔法”,讓您“覆蓋”非虛擬成員。像spring一樣使用System.Reflection.Emit構建代理,您還可以查看其ProxyFactoryObject的代碼。 這里是關於這個主題的另外三篇文章。
virtual
成員。看看PostSharp 。 我不知道有什么方法可以在 vanilla .Net 中執行您想要的操作,但是 PostSharp 提供了諸如“OnMethodBoundaryAspect”之類的東西,可用於替換或包裝方法中的代碼。
我用它來做日志、參數驗證、異常處理等事情。
有一個免費的社區版,應該適合您。 您需要將它安裝在您的開發機器以及您使用的任何構建服務器上。
另一種選擇是ContextBoundObject
。
大約 8-9 年前,有一篇關於 CodeProject 的文章使用這種方法來跟蹤方法調用。
為了在類中的每個函數之前和之后添加任何功能,Real proxy 是一個很好的方法。
所以現在在 T 中可以是任何 TestClass。 為 TestClass 創建這樣的實例-
var _instance=(object)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 Isolator 使用它來監視應用程序的執行,並且在調用方法時,CLR 會通知 typemock 隔離器,這允許 Isolator 完全覆蓋原始類。
.NET 6.0 向Reflection
命名空間添加了一個新的候選對象: DispatchProxy 。 團隊在這里宣布。 文章中包含一個示例用法。
您可以僅使用 System.Danymic 命名空間中的 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"];
}
}
然后在驅動 class 上:
dynamic dynamicProxy = new DynamicProxy(person);
這樣,您可以設置和獲取具有訪問控制的字段。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.