Метод мультиплексирования интерфейса вызывает в один делегат и демультиплексирует - PullRequest
2 голосов
/ 27 мая 2011

Краткая версия того, что я ищу, такова:

Я ищу механизм, который при наличии интерфейса будет динамически генерировать пару классов для этого интерфейса, один мультиплексор и один демультиплексор, который будет преобразовывать вызовы в / из делегата общего назначения следующей формы: object AnyCall( int method_selector, object[] arguments ). Класс мультиплексора примет делегат AnyCall в качестве параметра времени создания и реализует интерфейс, делегируя каждый вызов делегату. Класс демультиплексора примет ссылку на интерфейс в качестве параметра времени создания и реализует делегат, включив селектор метода и вызвав соответствующий метод интерфейса, передав ему параметры, которые были переданы делегату.

Длинная версия того, что я ищу, такова:

Рассмотрим следующий интерфейс:

public interface IFooable
{
    void Moo( int i );
    void Boo( string s, bool b );
}

И рассмотрим следующий класс, реализующий этот интерфейс:

public class FooImplementation: IFooable
{
    void IFooable.Moo( int i ) { System.Console.WriteLine( "i: " + i ); }
    void IFooable.Boo( string s, bool b ) { System.Console.WriteLine( "s: " + s + ", b: " + b );  }
}

А затем рассмотрите следующий класс, использующий вышеупомянутое:

public partial class MuxdemTest
{
    public static void InvokeFoo( IFooable fooable )
    {
        fooable.Moo( 42 );
        fooable.Boo( "fubar!", true );
    }

    public static void Run1()
    {
        IFooable fooable = new FooImplementation();
        InvokeFoo( fooable );
    }
}

Ничего особенного здесь.

Теперь давайте предположим, что вместо того, чтобы давать InvokeFoo () прямую ссылку на FooImplementation, я хотел бы встроить некоторую функциональность между ними, которая позволила бы мне делать некоторые действительно полезные вещи с вызовами метода интерфейса, например, считать количество вызовов одного из методов интерфейса; или преобразовать вызовы в сообщения и доставить их в FooImplementation, находящееся в другом потоке; или на другом континенте; или что угодно. И, конечно, эта промежуточная функциональность должна работать на ЛЮБОМ интерфейсе, а не только на IFooable.

Чтобы достичь этого, нам нужен способ мультиплексировать вызовы методов интерфейса в вызовы одного делегата общего назначения, а затем демультиплексировать их из вызовов этого делегата в вызовы методов интерфейса. Делегат, о котором я думаю, будет иметь следующую форму:

public delegate object AnyCall( int method_selector, object[] arguments );

Итак, вот реализация мультиплексора для интерфейса IFooable:

public class MuxForFooable: IFooable
{
    private readonly AnyCall AnyCall;
    public MuxForFooable( AnyCall anycall ) { AnyCall = anycall; }
    void IFooable.Moo( int i ) { AnyCall( 0, new object[]{ i } ); }
    void IFooable.Boo( string s, bool b ) { AnyCall( 1, new object[]{ s, b } ); }
}

А вот реализация демультиплексора для интерфейса IFooable:

public class DemuxForFooable
{
    public readonly IFooable Target;
    public DemuxForFooable( IFooable target ) { Target = target; }
    public object AnyCall( int method_selector, object[] arguments )
    {
        switch( method_selector )
        {
            case 0: Target.Moo( (int)arguments[0] ); break;
            case 1: Target.Boo( (string)arguments[0], (bool)arguments[1] ); break;
            default: throw new System.InvalidOperationException();
        }
        return null;
    }
}

А вот фрагмент кода, который использует вышеприведенное, достигая в точности того же, что и метод Run1 (), с той разницей, что все вызовы проходят через делегат AnyCall:

public partial class MuxdemTest
{
    public static void Run2()
    {
        IFooable fooable = new FooImplementation();
        DemuxForFooable demux = new DemuxForFooable( fooable );
        MuxForFooable mux = new MuxForFooable( demux.AnyCall );
        InvokeFoo( mux );
    }
}

Моя единственная проблема в том, что классы MuxForFooable и DemuxForFooable были написаны от руки, а я хочу, чтобы они генерировались динамически. Рассмотрим следующие фиктивные методы, которые будут генерировать их, и фрагмент кода, который будет использовать эти методы:

public partial class MuxdemTest
{
    public static T CreateMux<T>( AnyCall anycall )
    {
        if( typeof(T) == typeof(IFooable) )
            return (T)(IFooable)new MuxForFooable( anycall );
        throw new System.NotImplementedException();
    }

    public static AnyCall CreateDemux<T>( T target )
    {
        if( typeof(T) == typeof(IFooable) )
            return new DemuxForFooable( (IFooable)target ).AnyCall;
        throw new System.NotImplementedException();
    }

    public static void Run3()
    {
        IFooable fooable = new FooImplementation();
        AnyCall demux = CreateDemux<IFooable>( fooable );
        IFooable mux = CreateMux<IFooable>( demux );
        InvokeFoo( mux );
    }
}

Run3 () выше имеет точно такой же эффект, как Run2 () и Run1 () далее.

Итак, кто-нибудь когда-либо писал или знает, как написать реальные реализации для CreateMux () и CreateDemux ()?

Вы можете предположить, что интерфейсы будут содержать только методы и никакие свойства, и что все методы будут возвращать void и не будут принимать ни параметры ref, ни out. (Даже если бы я не возражал против реализации, которая позволила бы эти вещи.)

Есть много примеров кода, которые делают вещи, похожие на то, что я хочу, но я публикую этот вопрос в надежде, что кто-то уже сделал именно то, что я хочу, потому что тема динамического генерирования кода очень сложно.

Я знаю, что то, что я хочу, может быть достигнуто с помощью существующих прокси-фреймворков, таких как CastleProxy, но я не хочу использовать всю внешнюю фреймворк для такой простой задачи, и при этом я не хочу, чтобы из-за использования таких рамки. Я посмотрел на исходный код CastleProxy, пытаясь выяснить, как использовать его части, чтобы делать то, что я хочу, но я нашел его слишком сложным, чтобы из него можно было что-то понять.

Редактировать 1:

Шесть месяцев спустя я внедрил инструмент, который делает то, что я просил в этом вопросе.Я собираюсь опубликовать статью об этом, но прежде чем я это сделаю, мне нужна помощь с именами, поэтому я открыл еще один вопрос: Нужна помощь с именами (вызовы интерфейса маршалинга)

В то же время, пожалуйста, не стесняйтесь добавлять ответ, если он у вас есть.

Редактировать 2:

Я решил назвать инструмент "переплетаться" , иоперации "переплетение" и "распутывание" .Если вы хотите прочитать об этом, вот оно: michael.gr - Intertwine: нормализация вызовов интерфейса

Ответы [ 2 ]

1 голос
/ 27 мая 2011

Для достижения этого нам нужен способ мультиплексировать вызовы методов интерфейса в вызовы одного делегата общего назначения, а затем демультиплексировать их из вызовов этого делегата в вызовы методов интерфейса.

Я не понимаю, как это следует из ваших требований. Я думаю, что вы на самом деле хотите, это DynamixProxy , или что-то подобное. С его помощью вы можете перехватывать вызовы методов, что кажется именно тем, что вы хотите. Простой пример:

class LoggingIntereceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine(invocation.Method);
        invocation.Proceed();
    }
}

…

public static void Run1()
{
    var generator = new ProxyGenerator();
    IFooable fooable = generator.CreateInterfaceProxyWithTarget<IFooable>(
        new FooImplementation(), new LoggingIntereceptor());
    InvokeFoo(fooable);
}

Это дает следующий вывод:

Void Moo(Int32)
i: 42
Void Boo(System.String, Boolean)
s: fubar!, b: True

DynamicProxy генерирует тип, который реализует интерфейс во время выполнения, поэтому он должен быть довольно быстрым.

Если вы не хотите добавлять «целую структуру» в свои проекты, вам не нужно это делать. Для запуска приведенного выше примера требуется только одна дополнительная сборка: Castle.Core.dll.

0 голосов
/ 08 ноября 2013

Я только что прочитал oracle.com: Динамические прокси-классы и обнаружил, что среда выполнения Java предлагает половину того, что я ищу. Своими словами:

Динамический прокси-класс - это класс, который реализует список интерфейсов. указывается во время выполнения, так что вызов метода через один из интерфейсы на экземпляре класса будут закодированы и отправлены к другому объекту через единый интерфейс.

Соответствующее средство находится в пакете java.lang.reflect: класс Proxy создает мультиплексоры, а интерфейс InvocationHandler играет роль моего AnyCall делегата. Java, кажется, не предлагает никаких средств для создания демультиплексоров; почему, я не знаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...