Castle DynamicProxy:如何在代理接口时代理Equals?

10

我需要使用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能否实现这个功能?如何实现?


我正在使用DynamicProxy2 2.1.1。 - driis
2个回答

14
这有点棘手。查看一下代理如何工作的文档。接口代理包装一个对象并拦截到指定接口的调用。由于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);

谢谢你的解释。我想到可能是这样,接口没有定义Equals方法。我会尝试你的建议 :-) - driis

0
在你的示例中,你的类 Dummy 实现了 IDummy,但也提供了一个更具体的 Equals 覆盖。Krzysztof 的建议的另一种选择是通过让它实现 IEquatable<T> 将此方法拉入你的接口中,例如:
public interface IDummy : IEquatable<IDummy>
{
    string Name { get; set; }
}

这样,您的接口现在包括更具体的Equals覆盖,这意味着您生成的代理将根据需要代理对目标的调用。

显然,这并不能解决整个问题,只能允许您的代理转发对Equals(IDummy)而不是Equals(object)(或GetHashCode)的调用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接