通过Reflection.Emit生成代理仅在调试时启动

通过Reflection.Emit生成代理仅在调试时启动

问题描述:

大学的任务是使用Reflection.Emit实现简单的代理生成器/拦截器机制。 我想出了以下程序。通过Reflection.Emit生成代理仅在调试时启动

看来工作在Visual Studio中就好在调试模式[F5](调试 - >启动调试),但是当没有调试[Ctrl + F5]开始崩溃的大部分时间(调试 - >启动不调试)。

这两种模式有什么区别? (我不要参考调试<>发布模式)。 该问题发生在多台机器/设置(Win XP SP3 32位和64位,Windows 7 32位)上。

Click for pastebin。

// The proxy generator; I assume that the error is buried along the lines emitting the IL code 
public static class ProxyGenerator 
{ 
    public static T Create<T>(object obj, IInterception interception) 
    { 
     Type type = obj.GetType(); 

     TypeBuilder proxy = DefineProxy(type); 

     FieldBuilder wrappedField = DefinePrivateField(proxy, "wrappedObject", type); 
     FieldBuilder interceptionField = DefinePrivateField(proxy, "interception", interception.GetType()); 

     DefineConstructor(proxy, wrappedField, interceptionField); 
     DefineInterfaceMethods(type, proxy, wrappedField, interceptionField); 

     return (T) Activator.CreateInstance(proxy.CreateType(), obj, interception); 
    } 

    private static TypeBuilder DefineProxy(Type type) 
    { 
     var assemblyName = new AssemblyName {Name = "GeneratedProxyAssembly"}; 
     AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
      assemblyName, AssemblyBuilderAccess.Run); 

     ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("GeneratedProxyModule"); 

     return moduleBuilder.DefineType(
      type.Name + "Proxy", 
      type.Attributes, 
      typeof (object), 
      type.GetInterfaces()); 
    } 

    private static FieldBuilder DefinePrivateField(TypeBuilder typeBuilder, string fieldName, Type fieldType) 
    { 
     return typeBuilder.DefineField(fieldName, fieldType, FieldAttributes.Private); 
    } 

    private static void DefineConstructor(TypeBuilder typeBuilder, params FieldBuilder[] parameters) 
    { 
     ConstructorBuilder ctor = typeBuilder.DefineConstructor(
      MethodAttributes.Public, CallingConventions.Standard, parameters.Select(f => f.FieldType).ToArray()); 

     // Emit constructor 
     ILGenerator g = ctor.GetILGenerator(); 

     // Load "this" pointer and call base constructor 
     g.Emit(OpCodes.Ldarg_0); 
     g.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); 

     // Store parameters in private fields 
     for (int i = 0; i < parameters.Length; i++) 
     { 
      // Load "this" pointer and parameter and store paramater in private field 
      g.Emit(OpCodes.Ldarg_0); 
      g.Emit(OpCodes.Ldarg, i + 1); 
      g.Emit(OpCodes.Stfld, parameters[i]); 
     } 

     // Return 
     g.Emit(OpCodes.Ret); 
    } 

    private static void DefineInterfaceMethods(Type type, TypeBuilder proxy, FieldInfo wrappedField, FieldInfo interceptionField) 
    { 
     // Loop through all interface methods 
     foreach (MethodInfo interfaceMethod in type.GetInterfaces().SelectMany(i => i.GetMethods())) 
     { 
      MethodInfo method = type.GetMethod(interfaceMethod.Name); 

      MethodBuilder methodBuilder = proxy.DefineMethod(
       method.Name, 
       method.Attributes, 
       method.ReturnType, 
       method.GetParameters().Select(p => p.ParameterType).ToArray()); 

      // Emit method 
      ILGenerator g = methodBuilder.GetILGenerator(); 

      // Intercept before 
      EmitMethodCallOnMember(g, interceptionField, "Before", false); 

      // Delegate method call 
      EmitMethodCallOnMember(g, wrappedField, method.Name, true); 

      // Intercept after 
      EmitMethodCallOnMember(g, interceptionField, "After", false); 

      // Return 
      g.Emit(OpCodes.Ret); 
     } 
    } 

    private static void EmitMethodCallOnMember(ILGenerator g, FieldInfo field, string methodName, bool delegateParameters) 
    { 
     // Load "this" pointer to get address of field 
     g.Emit(OpCodes.Ldarg_0); 
     g.Emit(OpCodes.Ldflda, field); 

     MethodInfo method = field.FieldType.GetMethod(methodName); 
     if (delegateParameters) 
     { 
      // Load method parameters 
      for (int i = 0; i < method.GetParameters().Length; i++) 
      { 
       g.Emit(OpCodes.Ldarg, i + 1); 
      } 
     } 

     // Emit call 
     g.Emit(OpCodes.Call, method); 
    } 
} 

// Some infrastructure 
public interface IInterception 
{ 
    void Before(); 
    void After(); 
} 

public class LogInterception : IInterception 
{ 
    public void Before() 
    { 
     Console.WriteLine("Before ... "); 
    } 

    public void After() 
    { 
     Console.WriteLine("... After"); 
    } 
} 

public interface ITest 
{ 
    string DoSomething(string s1, string s2); 
} 

public class Test : ITest 
{ 
    public string DoSomething(string s1, string s2) 
    { 
     Console.WriteLine("... doing something ..."); 
     return s1 + s2; 
    } 
} 

// The test program, expected output is down below 

internal class Program 
{ 
    internal static void Main(string[] args) 
    { 
     var test = new Test(); 
     var proxy = ProxyGenerator.Create<ITest>(test, new LogInterception()); 

     Console.WriteLine(test.DoSomething("Hello", " World")); 
     Console.WriteLine("----------------------------------------"); 
     Console.WriteLine(proxy.DoSomething("Hello", " World")); 

     Console.ReadKey(); 
    } 
} 

另一个问题:缩小这些问题的最佳方法是什么? 我试图将生成的程序集保存到磁盘并在Reflector中打开生成的dll,但它看起来是空的。

如上所述,当在调试模式下启动程序似乎工作并打印以下输出。

... doing something ... 
Hello World 
---------------------------------------- 
Before ... 
... doing something ... 
... After 
Hello World 

感谢您的时间。

+0

你有什么例外?你的代码在调试和发布模式下都能正常工作。 – oxilumin 2011-05-07 20:28:34

+0

正如我所提到的,我没有提到调试或发布模式,而是“开始调试”和“无需调试即可开始”。 – 2011-05-07 21:01:47

+0

好的。我想我已经发现了这个问题。现在我真的不知道它为什么在调试模式下工作。 – oxilumin 2011-05-07 21:28:37

尝试在项目设置选项卡上明确设置x86模式。

只有在x64AnyCpu模式下运行程序时,我才得到致命异常。

啊,我明白了。将Ldflda替换为Ldfld。它工作正常,即使没有调试器(我只是跑.exe)。 Ldflda适用于您以refout关键字作为参数传入方法的字段。

+0

您是否参考了“平台目标”的设置?如果你这样做:'x86'已被选中。 – 2011-05-07 21:08:35

+0

我刚刚证实,这将使问题在我所有可用的平台上消失。 :) - 谢谢你,先生! – 2011-05-11 21:11:10