Краткая версия того, что я ищу, такова:
Я ищу механизм, который при наличии интерфейса будет динамически генерировать пару классов для этого интерфейса, один мультиплексор и один демультиплексор, который будет преобразовывать вызовы в / из делегата общего назначения следующей формы: 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: нормализация вызовов интерфейса