Използване на Reflection.Emit за внедряване на интерфейс

Да кажем, че имам следния интерфейс:

public interface IMyService
{
   void SimpleMethod(int id);
   int Hello(string temp);

}

И искате да генерирате клас, който изглежда така (използвайки излъчване на отражение).

public class MyServiceProxy : IMyService
{
  IChannel _channel;

  public MyServiceProxy(IChannel channel)
  {
    _channel = channel;
  }

  public void SimpleMethod(int id)
  {
    _channel.Send(GetType(), "SimpleMethod", new object[]{id});
  }

  public int Hello(string temp)
  {
    return (int)_channel.Request(temp);
  }
}

Как го правя? Проверих различни динамични проксита и фалшиви рамки. Те са малко сложни и не са много лесни за следване (и не искам външна зависимост). Не би трябвало да е толкова трудно да се генерира прокси за интерфейс. Може ли някой да ми покаже как?


person jgauffin    schedule 13.08.2010    source източник
comment
Динамичната прокси рамка трябва да бъде най-лесният начин за постигане на това. В противен случай бих предложил да компилирате вашата примерна реализация и да проверите генерирания IL чрез .NET Reflector или ILDASM. Това трябва да ви даде представа какво трябва да бъде излъчено.   -  person Daniel Pratt    schedule 13.08.2010
comment
100% съм съгласен с Даниел. Използвали ли сте DynamicProxy на Castle? Можете да задействате нещо в няколко реда код. Това наистина твърде сложно ли е? (И защо омразата към външните зависимости? Понякога най-добрият инструмент за работата вече е написан и е безплатен. Защо не го използвате?)   -  person Kirk Woll    schedule 13.08.2010
comment
Castle DynamicProxy е избраният инструмент и за мен в този случай. Поддържането на Reflection.Emit код не е забавно. Все пак уважавам загрижеността ви за зависимостите, но това е Майк Барнет, който ни даде ILMerge (например RhinoMocks използва DynamicProxy, но го обединява в техния сборник, за да подобри повторното използване).   -  person Just another metaprogrammer    schedule 13.08.2010


Отговори (1)


Като цяло ще се съглася с коментарите на другите. Използвал съм DynamicProxy на Castle и мисля, че е чудесен. Можете да направите някои наистина невероятни и мощни неща с него. Въпреки това, ако все още обмисляте да напишете свой собствен, прочетете:

Ако не се вълнувате от излъчването на IL, има някои нови техники, използващи ламбда изрази, които можете да използвате за генериране на код. Нищо от това обаче не е тривиална задача.

Ето пример за това как съм използвал ламбда изрази за генериране на динамичен манипулатор на събития за всяко .NET събитие. Бихте могли да използвате подобна техника за генериране на внедряване на динамичен интерфейс.

    public delegate void CustomEventHandler(object sender, EventArgs e, string eventName);

    Delegate CreateEventHandler(EventInfo evt, CustomEventHandler d)
    {
        var handlerType = evt.EventHandlerType;
        var eventParams = handlerType.GetMethod("Invoke").GetParameters();

        //lambda: (object x0, EventArgs x1) => d(x0, x1)

        // This defines the incoming parameters of our dynamic method.  
        // The method signature will look something like this:
        // void dynamicMethod(object x0, EventArgs<T> x1)
        // Each parameter is dynamically determined via the 
        // EventInfo that was passed.
        var parameters = eventParams.Select((p, i) => Expression.Parameter(p.ParameterType, "x" + i)).ToArray();

        // Get the MethodInfo for the method we'll be invoking *within* our
        // dynamic method.  Since we already know the signature of this method,
        // we supply the types directly.
        MethodInfo targetMethod = d.GetType().GetMethod(
            "Invoke", 
            new Type[] { typeof(object), typeof(EventArgs), typeof(string) }
            );

        // Next, we need to convert the incoming parameters to the types
        // that are expected in our target method.  The second parameter,
        // in particular, needs to be downcast to an EventArgs object
        // in order for the call to succeed.
        var p1 = Expression.Convert(parameters[0], typeof(object));
        var p2 = Expression.Convert(parameters[1], typeof(EventArgs));
        var p3 = Expression.Constant(evt.Name);

        // Generate an expression that represents our method call.  
        // This generates an expression that looks something like:
        // d.Invoke(x0, x1, "eventName");
        var body = Expression.Call(
            Expression.Constant(d),
            targetMethod,
            p1,
            p2,
            p3
        );

        // Convert the entire expression into our shiny new, dynamic method.
        var lambda = Expression.Lambda(body, parameters.ToArray());

        // Convert our method into a Delegate, so we can use it for event handlers.
        return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
    }

За разбирането,

-Дъг

person Doug    schedule 13.08.2010