Обтекание существующих объектов для перехвата вызовов методов / свойств в .NET - PullRequest
2 голосов
/ 18 декабря 2009

У меня есть ситуация, когда я хотел бы перехватывать вызовы свойств в .NET. Я смотрел на DynamicProxy в замке, и, кажется, работает нормально. Но для того, чтобы использовать его, мне нужно начать с нового объекта, то есть я не могу сделать что-то вроде этого:

MyType myType = new MyType();
myType.Property = "Test";

...

MyType wrappedMyType = proxyBuilder.Wrap(myType, new MyInterceptor());
wrappedMyType.Property = "Test2";

Я что-то пропустил?

EDIT:

О Боже, это, конечно, должно быть завернуто MyType. Большая ошибка. Сожалею. (

Ответы [ 4 ]

3 голосов
/ 18 декабря 2009

Это не работает так, это никак не меняет исходный объект.

Думай об этом так. Давайте рассмотрим переезд в Китай и работу в китайской компании, которая будет выплачивать вашу зарплату только на банковский счет в китайском банке.

Итак, вам нужно получить счет в китайском банке. Проблема в том, что банк, который вы хотите использовать, не говорит по-английски, поэтому у вас есть проблема.

Что бы вы могли сделать, если бы это было возможно, это вызвать прокси-службу, службу переводчика, которая от вашего имени звонит в банк. Все, что вы скажете этому доверенному лицу, будет переведено на китайский язык и передано сотруднику банка. Все, на что он / она отвечает на китайском, будет переведено обратно на английский и с вами разговаривает.

По сути, теперь вы можете делать что-то по линии связи при разговоре с вашим банком.

Однако из-за этого сотрудники вашего банка не говорят по-английски.

Прокси-объект из вашего примера не изменяет базовый объект. Каждый раз, когда вы вызываете методы для ваших прокси-объектов, они, в свою очередь, будут вызывать методы для базового объекта, возможно, выполняя обработку в процессе.

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

1 голос
/ 18 декабря 2009

Вы не можете сделать это, и по уважительным причинам. Это не относится к замку Виндзор. Проблема заключается в том, что у вас нет гарантии, что методы помечены как virtual, и поэтому у вас есть несоответствие, когда какое-то состояние поступает из обернутого объекта, а какое-то состояние - из прокси-объекта.

Вспомните следующий очень простой пример:

abstract class AbstractPerson {
    public int Age { get; protected set; }
    public abstract void Birthday();
}

class Person : AbstractPerson {
    public Person(int age) { Age = age; }
    public override Birthday() { Age++; }
}

Допустим, мы хотим создать прокси для AbstractPerson для перехвата Birthday.

class PersonProxy : AbstractPerson {
    readonly AbstractPerson wrappedPerson;

    public PersonProxy(AbstractPerson person) { 
        wrappedPerson = person;
    }
    public override void Birthday() {
        DoInterceptors();
        wrappedPerson.Birthday();
    }
    public void DoInterceptors() { 
        // do interceptors 
    }
}

Обратите внимание, что мы не можем переопределить Age, потому что он не помечен как virtual. Вот откуда появятся противоречивые состояния:

Person knuth = new Person(71);
PersonProxy proxy = new PersonProxy(knuth);
Console.WriteLine(knuth.Age);
knuth.Birthday();
Console.WriteLine(knuth.Age);
Console.WriteLine(proxy.Age);

Это напечатает

71
72
0

к консоли. Что случилось? Поскольку Age не помечен как виртуальный, наш прокси-объект не может переопределить базовое поведение и вызвать wrappedPerson.Age. Этот пример даже показывает, что добавление Age = wrappedPerson.Age в конструктор для PersonProxy не поможет. Наш прокси на самом деле не прокси. Вот почему вы не можете обернуть существующие объекты.

0 голосов
/ 18 декабря 2009

PostSharp может использоваться для вас , в зависимости от того, что именно вы хотите сделать с помощью "перехвата", и если вы можете изменить исходный код.

Чтобы этот вариант был жизнеспособным, вы должны иметь возможность добавлять атрибуты к исходным свойствам, которые вы хотите перехватить. (Я предполагаю, что это не вариант в вашем случае, но не могу сказать наверняка.) Если вы в состоянии сделать это, вы можете создать атрибут (производный от OnMethodBoundaryAspect), который может одновременно устанавливать 'ReturnValue' и FlowBehavior, чтобы вы эффективно перехватили вызов.

0 голосов
/ 18 декабря 2009

Возможно, вы могли бы сделать это с помощью System.Reflection.Emit.TypeBuilder, но это было бы непросто и, вероятно, не сработало бы для всех типов. Например, вы не можете сделать это на запечатанных типах, потому что для поддержания возможности использовать ваш тип в обычном порядке вам придется наследовать от него в создаваемом вами типе, и вам придется либо переопределять, либо скрывать каждое свойство базового класса , Вдобавок ко всему, вам придется выдавать IL в тело методов набора свойств, когда вы переопределяете его, чтобы вызвать событие или что-то в этом роде.

Все это возможно, но не просто и не идеально. Вам, вероятно, будет лучше с другим решением. Мне действительно нравятся такие вещи, хотя, может быть, если у меня будет время, я обновлю этот ответ примером кода (извините, я работаю).

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

...