Castle DynamicProxy: Как использовать Proxy Equals при прокси интерфейса? - PullRequest
10 голосов
/ 03 июня 2010

Мне нужно использовать 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? Как?

Ответы [ 2 ]

12 голосов
/ 04 июня 2010

Это немного сложно. Посмотрите документацию о том, как работают прокси. Интерфейсные прокси обертывают объект и перехватывают вызовы к назначенному интерфейсу (ам). Поскольку Equals не является частью этого интерфейса, второй вызов функции equals сравнивает прокси, а не их цели.

Так, что обеспечивает реализацию для второго Equals вызова?

Proxy - это просто еще один класс, реализующий ваш 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);
0 голосов
/ 06 июля 2011

в вашем образце; ваш класс Dummy реализует IDummy, но также предоставляет более конкретное переопределение Equals. Альтернативой предложению Кшиштофа является использование этого метода в вашем интерфейсе с помощью реализации IEquatable<T>, например:

public interface IDummy : IEquatable<IDummy>
{
    string Name { get; set; }
}

Таким образом, ваш интерфейс теперь включает более конкретное Equals переопределение, что означает, что ваш сгенерированный прокси будет прокси-вызовы к вашей цели по мере необходимости.

Очевидно, что это не решит всей проблемы и позволит вашему прокси-серверу только перенаправлять вызовы на Equals(IDummy), а не на Equals(object) (или GetHashCode в этом отношении).

...