繁体   English   中英

发出对lambda表达式的调用

[英]Emit a call to a lambda expression

当我尝试调用lambda表达式时,出现异常。 lambda生成一个私有的静态方法似乎是事实。 我怎样才能实现自己想要的?

    static void Main(string[] args)
    {
        var typeBuilder = CreateTypeBuilder();
        AddStaticMethodAsAProperty(typeBuilder, "StaticPublic", Return1);
        AddStaticMethodAsAProperty(typeBuilder, "StaticPrivate", Return2);
        AddStaticMethodAsAProperty(typeBuilder, "Lambda", () => 3);

        var newType = typeBuilder.CreateType();
        dynamic newObject = Activator.CreateInstance(newType);
        var resultFromStatic = newObject.StaticPublic; //Ok
        var resultFromStaticPrivate = newObject.StaticPrivate; //Additional information: Attempt by method 'NewType.get_StaticPrivate()' to access method 'Test.Program.Return2()' failed.
        var resultFromLambda = newObject.Lambda; //failed with Additional information: Attempt by method 'NewType.get_Lambda()' to access method 'Test.Program.<Main>b__3()' failed.
    }

    public static void AddStaticMethodAsAProperty(TypeBuilder typeBuilder, string propertyName, Func<int> methodToAdd)
    {
        var propertyType = methodToAdd.Method.ReturnType;

        var getPropertyMethodBuilder = typeBuilder.DefineMethod(string.Format("get_{0}", propertyName), MethodAttributes.Public | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);

        var getIL = getPropertyMethodBuilder.GetILGenerator();

        getIL.Emit(OpCodes.Call, methodToAdd.Method);
        getIL.Emit(OpCodes.Ret);

        var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, new Type[] { });
        propertyBuilder.SetGetMethod(getPropertyMethodBuilder);
    }

    public static int Return1()
    {
        return 1;
    }

    private static int Return2()
    {
        return 1;
    }

    private static TypeBuilder CreateTypeBuilder()
    {
        var newTypeName = "NewType";
        var newAssemblyName = new AssemblyName(newTypeName);
        var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(newAssemblyName, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        var typeBuilder = moduleBuilder.DefineType(newTypeName
                                            , TypeAttributes.Public |
                                            TypeAttributes.Class |
                                            TypeAttributes.AutoClass |
                                            TypeAttributes.AnsiClass |
                                            TypeAttributes.BeforeFieldInit |
                                            TypeAttributes.AutoLayout
                                            , null);
        return typeBuilder;
    }

methodToAdd是一个委托。 您正在尝试调用内部包含的Method ,但这是错误的方法。 您要改为执行委托,这有点复杂。

基本上,您需要在methodToAddInvoke方法。 这也意味着您需要将委托放置在最终代码可访问的位置。 换句话说,您需要完成C#编译器通常要做的许多工作-创建包含对委托的引用的匿名类。 调用callvirt Func<int>.Invoke在该引用上进行调用很容易。

值得庆幸的是,新的Expression东西完全很棒。 因此,您应该可以执行以下操作:

Expression<Func<T>> expr = () => methodToAdd();
expr.CompileToMethod(getPropertyMethodBuilder);

始终尝试尽可能避免产生IL-即使ilasm本身也在做ILGenerator没有做的很多工作。 但是它仍然必须产生有效的IL-并且在错误的范围内call私有方法当然不是有效的IL。

我找到了我想做的,想法是跟踪该函数作为字段,然后在getter中调用此字段。

  static void Main(string[] args)
    {
        var typeBuilder = CreateTypeBuilder();
        AddStaticMethodAsAProperty(typeBuilder, "StaticPublic", Return1);
        AddStaticMethodAsAProperty(typeBuilder, "StaticPrivate", Return2);
        AddStaticMethodAsAProperty(typeBuilder, "Lambda", () => 3);

        var newType = typeBuilder.CreateType();

        dynamic newObject = Activator.CreateInstance(newType);
        foreach (var onNewObjectCreated in _onNewObjectCreated)
        {
            onNewObjectCreated(newObject);
        }

        var resultFromStatic = newObject.StaticPublic; //Ok 1
        var resultFromStaticPrivate = newObject.StaticPrivate; //Ok 2
        var resultFromLambda = newObject.Lambda; //Ok 3
    }

    private static List<Action<object>> _onNewObjectCreated = new List<Action<object>>();
    public static void AddStaticMethodAsAProperty<T>(TypeBuilder typeBuilder, string propertyName, Func<T> valueGetter)
    {
        var propertyType = valueGetter.Method.ReturnType;
        var delegateFieldBuilder = typeBuilder.DefineField(string.Format("_backingDelegate{0}", propertyName), valueGetter.GetType(), FieldAttributes.Private);

        var getMethod = typeBuilder.DefineMethod(string.Format("get_{0}", propertyName), MethodAttributes.Public | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);


        Action<object> setDelegateFieldAction = newlyCreatedObject => { newlyCreatedObject.GetType().GetField(delegateFieldBuilder.Name, BindingFlags.NonPublic | BindingFlags.Instance).SetValue(newlyCreatedObject, valueGetter); };
        _onNewObjectCreated.Add(setDelegateFieldAction);

        var il = getMethod.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);//stack [this]
        il.Emit(OpCodes.Ldfld, delegateFieldBuilder);//stack [this._backingDelegateXXX]
        il.Emit(OpCodes.Callvirt, valueGetter.GetType().GetMethod("Invoke"));//stack [valueReturnedByTheDelegate]
        il.Emit(OpCodes.Ret);
        var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, new Type[] { });
        propertyBuilder.SetGetMethod(getMethod);
    }

    public static int Return1()
    {
        return 1;
    }

    private static int Return2()
    {
        return 2;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM