I'm trying to call the deserialization constructor for an object that already exists. How do I do that with expression trees?
I tried:
// 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 });
but Expression.Call
only accepts MethodInfo
not ConstructorInfo
, so that doesn't work - unless there is a way to convert to a MethodInfo
?
Update
I eneded up just using 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.
Update
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++;
}
}
}
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.