如何通过现有对象上的表达式树调用构造函数?

sir*_*lot 5 c# serialization 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 });
Run Code Online (Sandbox Code Playgroud)

Expression.Call只接受MethodInfoConstructorInfo,所以这不起作用 - 除非有办法转换为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() });
Run Code Online (Sandbox Code Playgroud)

我害怕它的表现,但它似乎是唯一的方法.

更新

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

def*_*mer 8

如果要使用表达式树,请使用Expression.New.这是一个例子

var info = Expression.Parameter(typeof(SerializationInfo), "info");
var context = Expression.Parameter(typeof(StreamingContext), "context");

var callTheCtor = Expression.New(ctorInfo, info, context);
Run Code Online (Sandbox Code Playgroud)

这不适用于现有对象,但由于您的代码显示GetUninitializedObject我认为您可以删除该部分并用于Expression.New创建新对象.


小智 6

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

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++;
        }
    }   
}
Run Code Online (Sandbox Code Playgroud)

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