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

Jul*_*ner 4 reflection.emit .net-4.0 visual-studio-2010

大学的一项任务是使用Reflection.Emit实现一个简单的代理生成器/拦截器机制.我想出了以下程序.

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

这两种模式有什么区别?(我没有提到Debug <> Release模式).问题出现在多台机器/设置上(Win XP SP3 32位和64位,Windows 7 32位).

单击以获取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();
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个问题:缩小此类问题的最佳方法是什么?我试图将生成的程序集保存到磁盘并在Reflector中打开生成的dll,但它似乎是空的.

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

... doing something ...
Hello World
----------------------------------------
Before ...
... doing something ...
... After
Hello World
Run Code Online (Sandbox Code Playgroud)

谢谢你的时间.

oxi*_*min 6

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

只有在运行程序x64AnyCpu模式时才会收到致命异常.

啊,我知道了.替换LdfldaLdfld.即使没有调试器也能正常工作(我只是运行.exe). Ldflda用于作为参数传递给方法的字段refout关键字.