Трудности, связанные с отсутствием методов в C # 4.0: динамический против RealProxy - PullRequest
6 голосов
/ 07 июня 2009

Кто-нибудь знает способ перехвата dynamic вызовов методов (особенно тех, которые собираются поднять RuntimeBinderException с) с RealProxy? Я надеялся поймать исключение и реализовать «метод, отсутствующий» поверх этого, но, похоже, он генерируется до того, как перехватчик заглянет в него.

Мой тест выглядит так:

dynamic hello = MethodMissingInterceptor<DynamicObject>.Create();
Assert.AreEqual("World", hello.World());

Где World на самом деле не реализовано в DynamicObject. Перехватчик довольно прост - я надеялся проверить IMethodReturnMessage.Exception для RuntimeBinderException и перейти к чему-то вроде:

public IMessage MethodMissing(IMethodCallMessage call)
{
    return new ReturnMessage(call.MethodBase.Name, new object[0], 0, call.LogicalCallContext, call);
}

К сожалению, все, что я вижу в моем перехватчике, это некоторые вызовы GetType, а не несуществующий метод World.

Если это не удастся - кто-нибудь знает, если на .NET 4.0 успешно работает версия DynamicProxy, которая могла бы решить проблему?

1 Ответ

17 голосов
/ 10 июня 2009

Начну с длинного ответа. Каждая привязка динамической операции в C # делает примерно эти три вещи в следующем порядке:

  1. Попросить объект связать себя, если он реализует IDynamicMetaObjectProvider или является COM-объектом, и если это не удается, то ...
  2. Свяжите операцию с операцией над простым-старым-clr-объектом, используя отражение, и если это не удастся, то ...
  3. Возвращает объект DynamicMetaObject, представляющий полный сбой привязки.

Вы видите вызовы GetType, потому что на шаге 2 связыватель времени выполнения C # размышляет над вами, чтобы попытаться выяснить, есть ли у вас метод "World", который подходит для вызова, и это происходит из-за реализации IDynamicMetaObjectProvider Привет, если есть, не может придумать что-то особенное, чтобы сделать.

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

Так что эта стратегия не сработает для вас, если вы хотите реализовать method_missing в C #. У вас есть несколько вариантов.

Одним простым вариантом является реализация IDynamicMetaObjectProvider в вашем MethodMissingInterceptor и переход к реализации IDMOP обернутого объекта. В случае сбоя со стороны внутренней IDMOP вы можете связать с чем угодно (возможно, вызовом метода method_missing, хранящегося в перехватчике). Недостатком здесь является то, что это работает только для объектов, которые, как известно, являются динамическими объектами, например те, которые реализуют IDMOP для начала. Это потому, что вы в основном вставляете себя между шагами 1 и 2.

Еще одна альтернатива, о которой я могу подумать, - это реализовать IDynamicMetaObjectProvider и в нем положительно реагировать на каждое связывание, возвращая вызов метода, который (a) генерирует тот же код, который компилятор C # сгенерировал бы для связывания в первую очередь и (b) перехватывает RuntimeBinderException для вызова метода method_missing. Недостатком здесь является то, что это будет довольно сложно - вам нужно будет генерировать произвольные типы делегатов и IL, который их использует, для открытых типов в сборке компоновщика времени выполнения C #, которые, честно говоря, не предназначены для общественного потребления. Но, по крайней мере, вы получите метод отсутствующий для всех операций.

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

Суть проблемы в том, что C # 4.0 не имеет дизайна, который предвосхищает ваше желание сделать это. В частности, вы не можете легко вставить себя между шагами 2 и 3. Это подводит меня к короткому ответу, извините, в C # 4.0 нет method_missing.

...