Замок DynamicProxy: получить незафиксированный объект
Я использую Castle DynamicProxy, чтобы добавить перехватчик для моих типов. Теперь мне нужно получить базовый базовый тип (НЕ сам прокси).
Я нашел несколько советов по SO, которые предлагали использовать класс ProxyUtil следующим образом:
object realInstance = ProxyUtil.GetUnproxiedInstance(proxyInstance);
Это не похоже на работу
bool isProxy = ProxyUtil.IsProxy(realInstance);
всегда верно.
Я также попытался использовать следующий фрагмент кода, который, по сути, и делает ProxyUtil:
var accessor = proxyInstance as IProxyTargetAccessor;
var realInstance = accessor.DynProxyGetTarget();
с такими же результатами realInstance по-прежнему является прокси.
Что мне здесь не хватает?
3 ответа
Этот вопрос немного старый, но, надеюсь, мое решение (которое опирается на.NET 4+) кому-то поможет.
Создав прокси следующим образом:
ProxyGenerator generator = new ProxyGenerator();
MyClass proxy = generator.CreateClassProxyWithTarget(underlying, new MyInterceptor(this));
Я смог получить основную цель с помощью следующего метода:
internal static TType UnwrapProxy<TType>(TType proxy)
{
if (!ProxyUtil.IsProxy(proxy))
return proxy;
try
{
dynamic dynamicProxy = proxy;
return dynamicProxy.__target;
}
catch (RuntimeBinderException)
{
return proxy;
}
}
Он опирается на внутреннюю реализацию Castle - то есть, что сгенерированный прокси имеет член __target. Это красиво и самодостаточно, хотя и подкреплено юнит-тестом или двумя, мы должны отследить любые изменения, если более поздняя версия Castle сломает это. Это использует v3.2.0.0 Castle.
Если у вас нет invocation
объект доступен, или если вы наблюдаете 'Castle.Proxies.IXeroServiceProxy.__target' is inaccessible due to its protection level
пытаясь ответить @s1mm0t, вы можете попробовать следующий код, который использует отражение вместо dynamic
:
public static T UnwrapProxy<T>(T proxy) {
if(!ProxyUtil.IsProxy(proxy)) {
return proxy;
}
try {
const System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
var proxyType = proxy.GetType();
var instanceField = proxyType.GetField("__target", flags);
var fieldValue = instanceField.GetValue(proxy);
return (T) fieldValue;
}
catch(RuntimeBinderException) {
return proxy;
}
}
Я просто использую такой интерфейс
public interface IMarker
{
Type ActualType { get; }
}
добавьте его в мой прокси и перехватите:
public class MyInterceptor<T> : IInterceptor
...
if (invocation.Method.DeclaringType == typeof(IMarker))
{
if (invocation.Method.Name.Equals("get_ActualType"))
{
invocation.ReturnValue = typeof(T);
return;
}
}
Так что, в конце концов, мне нужно только проверить
if( obj is IMarker )
Type t = (obj as IMarker).ActualType`
Возможно, есть лучшие варианты сделать это, но это работает и держит мой код в чистоте от ссылок на замки. Надеюсь это поможет.
Если вы создаете прокси на основе наследования (generator.CreateClassProxy<MyClass>()
) прокси является целью.