如何通过现有对象上的表达式树调用构造函数? [英] How do you call a constructor via an expression tree on an existing object?
问题描述
我正在尝试为已经存在的对象调用反序列化构造函数.我如何用表达式树做到这一点?
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屋!