通过Reflection.Emit生成代理仅在调试开始时起作用

|| 大学的一项任务是使用Reflection.Emit实现一个简单的代理生成器/拦截器机制。 我想出了以下程序。 在Visual Studio中,在调试模式
[F5]
(调试->开始调试)下,它似乎可以正常工作,但是在不调试
[Ctrl + F5]
(调试->未经调试开始)的情况下启动时,大多数时间会崩溃。 这两种模式有什么区别? (我不参考Debug <> Release模式)。 在多台计算机/设置(Win XP SP3 32位和64位,Windows 7 32位)上会发生此问题。 单击以粘贴。
// 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
谢谢你的时间。     
已邀请:
尝试在项目设置选项卡上显式设置“ 4”模式。 只有在
x64
AnyCpu
模式下运行程序时,我才出现致命异常。 啊,我知道了。将
Ldflda
替换为
Ldfld
。即使没有调试器,它也能正常工作(我只是运行.exe)。
Ldflda
用于使用
ref
out
关键字作为参数传递给方法的字段。     

要回复问题请先登录注册