如何通过现有对象上的表达式树调用构造函数? [英] How do you call a constructor via an expression tree on an existing object?

查看:40
本文介绍了如何通过现有对象上的表达式树调用构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为已经存在的对象调用反序列化构造函数.我如何用表达式树做到这一点?

I'm trying to call the deserialization constructor for an object that already exists. How do I do that with expression trees?

我试过了:

// Create an uninitialized object
T graph = (T)FormatterServices.GetUninitializedObject(graphType);

// (graph, serializationInfo, streamingContext) => graph.Constructor(serializationInfo, streamingContext)
ParameterExpression graphParameter = Expression.Parameter(serializationPack.SelfSerializingBaseClassType, "graph");
ParameterExpression serializationInfoParameter = Expression.Parameter(typeof(SerializationInfo), "serializationInfo");
ParameterExpression streamingContextParameter = Expression.Parameter(typeof(StreamingContext), "streamingContext");

MethodCallExpression callDeserializationConstructor = Expression.Call(graphParameter,
    (MethodInfo)serializationPack.SelfSerializingBaseClassType.GetConstructor(new[] { typeof(SerializationInfo), typeof(StreamingContext) }), 
        new[] { serializationInfoParameter, streamingContextParameter });

但是 Expression.Call 只接受 MethodInfo 而不是 ConstructorInfo,所以这不起作用 - 除非有办法转换为MethodInfo?

but Expression.Call only accepts MethodInfo not ConstructorInfo, so that doesn't work - unless there is a way to convert to a MethodInfo?

更新

我只使用了 ConstructorInfo.Invoke:

// Cache this part
ConstructorInfo deserializationConstructor = serializationPack
    .SelfSerializingBaseClassType
    .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard,
        new[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);

// Call this when I need it
deserializationConstructor.Invoke(graph, new Object[] { serializationInfo, new StreamingContext() });

我害怕它的性能,但它似乎是唯一的方法.

I'm scared of the performance on it, but it seems to be the only way to do this.

更新

现在有了正确的答案.谢谢大家.

This has a proper answer now. Thanks all.

推荐答案

如果我没看错你的问题,你并不真正关心构造函数是否是通过表达式树调用的,只要实际调用不是'需要反思.您可以构建一个转发到构造函数调用的动态方法:

If I'm reading your question correctly, you don't really care whether the constructor is called via an expression tree, so long as the actual invocation doesn't require reflection. You can build a dynamic method that forwards to a constructor call:

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication1
{
    static class Program
    {
        static void Main(string[] args)
        {
            var constructor = typeof(Foo).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
            var helperMethod = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(Foo) }, typeof(Foo).Module, true);
            var ilGenerator = helperMethod.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Call, constructor);
            ilGenerator.Emit(OpCodes.Ret);
            var constructorInvoker = (Action<Foo>)helperMethod.CreateDelegate(typeof(Action<Foo>));

            var foo = Foo.Create();
            constructorInvoker(foo);
            constructorInvoker(foo);
        }
    }

    class Foo
    {
        int x;

        public static Foo Create()
        {
            return new Foo();
        }

        private Foo()
        {
            Console.WriteLine("Constructor Foo() called, GetHashCode() returns {0}, x is {1}", GetHashCode(), x);
            x++;
        }
    }   
}

请注意,这就像一个常规的方法调用.x 在打印其值之前未设置,因此当您再次调用构造函数时它不会重置为 0.根据您的构造函数的作用,这可能是也可能不是问题.

Note though that this behaves like a regular method call. x is not set before printing its value, so it does not get reset to 0 when you call the constructor again. Depending on what your constructor does, this may or may not be a problem.

这篇关于如何通过现有对象上的表达式树调用构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆