我需要使用Castle DynamicProxy通过向ProxyGenerator.CreateInterfaceProxyWithTarget提供它的实例来代理接口。我还需要确保调用Equals,GetHashCode和ToString命中具体实例上的方法,我正在传递,而且我无法使其工作。
换句话说,我希望这个小样本能够打印True
两次,而实际上它会打印True,False
:
using System;
using Castle.Core.Interceptor;
using Castle.DynamicProxy;
public interface IDummy
{
string Name { get; set; }
}
class Dummy : IDummy
{
public string Name { get; set; }
public bool Equals(IDummy other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Name, Name);
}
public override bool Equals(object obj)
{
return Equals(obj as IDummy);
}
}
class Program
{
static void Main(string[] args)
{
var g = new ProxyGenerator();
IDummy first = new Dummy() {Name = "Name"};
IDummy second = new Dummy() {Name = "Name"};
IDummy firstProxy = g.CreateInterfaceProxyWithTarget(first, new ConsoleLoggerInterceptor());
IDummy secondProxy = g.CreateInterfaceProxyWithTarget(second, new ConsoleLoggerInterceptor());
Console.WriteLine(first.Equals(second));
Console.WriteLine(firstProxy.Equals(secondProxy));
}
}
internal class ConsoleLoggerInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("Invoked " + invocation.Method.Name);
}
}
DynamicProxy可以实现吗?怎么样?
答案 0 :(得分:12)
这有点棘手。关于代理如何工作的Take a look at documentation。接口代理包装对象并拦截对指定接口的调用。由于Equals
不是该接口的一部分,因此对equals的第二次调用是比较代理,而不是它们的目标。
那么是什么提供了第二次Equals
调用的实现?
代理只是实现IDummy
接口的另一个类。与任何类一样,它也有一个基类,这是被调用的Equals
的基本实现。默认情况下,此基类是System.Object
我希望你现在能看到它的发展方向。解决此问题的方法是告诉代理实现一些代理感知基类,该基类将调用转发给代理目标。部分实现可能如下所示:
public class ProxyBase
{
public override bool Equals(object obj)
{
var proxy = this as IProxyTargetAccessor;
if (proxy == null)
{
return base.Equals(obj);
}
var target = proxy.DynProxyGetTarget();
if (target == null)
{
return base.Equals(obj);
}
return target.Equals(obj);
}
// same for GetHashCode
}
现在,您只需要指示代理生成器将此基类用于接口代理,而不是默认值。
var o = new ProxyGenerationOptions();
o.BaseTypeForInterfaceProxy = typeof(ProxyBase);
IDummy firstProxy = g.CreateInterfaceProxyWithTarget(first, o);
IDummy secondProxy = g.CreateInterfaceProxyWithTarget(second, o);
答案 1 :(得分:0)
在你的样本中;您的班级Dummy
会实施IDummy
,但也会提供更具体的Equals
覆盖。 Krzysztof的建议的替代方法是通过实现IEquatable<T>
将此方法引入您的界面,例如:
public interface IDummy : IEquatable<IDummy>
{
string Name { get; set; }
}
这样,您的界面现在包含更具体的Equals
覆盖,这意味着您生成的代理将根据需要代理对目标的调用。
显然,这并不能解决整个问题,并且只允许您的代理将呼叫转发至Equals(IDummy)
而不是Equals(object)
(或GetHashCode
。)