2010-12-04 3 views
5

Рассмотрим следующий тривиальный код:Ввод кода впрыска с использованием DynamicMethod?

using System; 
class Test 
{ 
    delegate int FooDelegate(int i); 
    FooDelegate Foo = FooImplementation; 
    static int FooImplementation(int i) 
    { 
     return i + 1; 
    } 

    public static void Main() 
    { 
     Foo(1); 
    } 
} 

То, что я хотел бы сделать, это придать некоторый отладочный код в делегатом Foo, который был бы эквивалентен:

FooDelegate Foo = delegate(int i) 
{ 
    try 
    { 
     DebugPrologue(); 
     return FooImplementation(i); 
    } 
    finally 
    { 
     DebugEpilogue(); 
    } 
}; 

Твист является то, что я должен быть в состоянии сделать это на runtime, поэтому методы компиляции и пост-обработки не могут быть и речи.

Мой первоначальный подход использовал Delegate.Combine() для добавления методов пролога и эпилога в делегат Foo. Увы, это не сработает, так как он возвращает возвращаемые значения.

Моя нынешняя идея - использовать System.Reflection.Emit и DynamicMethod в качестве потенциального решения. Насколько я могу судить, мне нужно получить MethodInfo для FooImplementation, получить его MethodBody, преобразовать его в DynamicMethod и вставить в него блок try-finally.

К сожалению, я не знаю, как это сделать. Любой, кто хочет протянуть руку? Или у вас есть другая (желательно более простая) идея?

Редактировать: прецедент здесь отлаживает привязку OpenGL (http://www.opentk.com). Мы должны вводить 2226 методов с совершенно разными параметрами, поэтому необходим общий подход.

ответ

0

Что я, в конце концов, сделал, это реализовать свое собственное решение для перезаписи с использованием Mono.Cecil.Просто, быстро и отлично работает. К сожалению, это должно быть сделано как событие после сборки, поэтому на самом деле это не инъекция кода времени исполнения.

0

Примечание уверен, что вы пытаетесь сделать, но если это просто перенаправляет FooImplementation, который указал на на Foo поле, то вы можете просто сделать:

class Test 
{ 
    delegate int FooDelegate(int i); 
    static FooDelegate Foo = FooImplementation; 

    static int FooImplementation(int i) 
    { 
     return i + 1; 
    } 

    public static void Main() 
    { 
     Inject(); 
     Foo(1); 
    } 


    public static void Inject() 
    { 
     var oldImpl = Foo; 

     Foo = i => 
      { 
       try 
       { 
        BeginDebug(); 
        return oldImpl(i); 
       } 
       finally 
       { 
        EndDebug(); 
       } 
      }; 
    } 

    public static void BeginDebug() 
    { 
     Console.WriteLine("Begin Foo"); 
    } 

    public static void EndDebug() 
    { 
     Console.WriteLine("End Foo"); 
    } 
} 

Конечно, Inject не должны быть в том же классе, однако, если поле/свойство не является общедоступным, вам придется использовать Reflection, чтобы сделать это, но все же не так сложно.

+0

Это решение прекрасно работает для приемлемого количества методов для инъекций. К сожалению, в этом конкретном случае я должен ввести 2226 методов с совершенно разными параметрами (это привязка OpenGL, я обновлю исходный вопрос, чтобы это отразить). – 2010-12-04 23:21:47

0

Считаете ли вы, что-то вроде: Postsharp? http://www.sharpcrafters.com/postsharp

или корпоративная политика библиотеки Инъекция? http://msdn.microsoft.com/en-us/library/ff650672.aspx

или даже Mono.Cecil? http://www.mono-project.com/Cecil

+0

Mono.Cecil и postharp запускаются во время компиляции, которые здесь не будут работать (мне нужно отключить/включить это во время выполнения). Я раньше не посещал корпоративную библиотеку, спасибо, я проверю ее. – 2010-12-04 23:14:41

0

TypeMock также может быть жизнеспособным решением вашей проблемы: http://www.typemock.com/

Вы можете также использовать RealProxy класс (http://msdn.microsoft.com/en-us/library/system.runtime. remoting.proxies.realproxy.aspx), чтобы создать собственный прокси-сервер во время выполнения, который сначала вызовет ваш введенный код, а затем вызовет фактический метод.

3

Вы можете использовать выражения.

var param = Expression.Parameter(typeof(int), "i"); 

Foo = Expression.Lambda(
     Expression.TryFinally(
      Expression.Block(
       <expression for DebugPrologue>, 
       Expression.Invoke(<expression for FooImplementation>, param)), 
      <expression for DebugEpilogue>), 
     param) 
     .Compile(); 

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

+0

Спасибо, очень просто и элегантно. Позвольте мне проверить это решение, я думаю, он должен работать! – 2010-12-05 10:40:50

0

Вы также можете попробовать CInject на codeplex, чтобы с легкостью вводить код в управляемый код (C# или VB.NET).