Sto cercando di chiamare il costruttore di deserializzazione per un oggetto che esiste già. Come faccio a farlo con gli alberi di espressione?
Provai:
// 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 });
ma Expression.Call
accetta solo MethodInfo
not ConstructorInfo
, quindi non funziona - a meno che non ci sia un modo per convertire in un MethodInfo
?
Aggiornare
Ho sviluppato semplicemente usando 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() });
Ho paura delle prestazioni su di esso, ma sembra essere l'unico modo per farlo.
Aggiornare
Questo ha una risposta corretta ora. Ringrazia tutti.
Se sto leggendo la tua domanda correttamente, non ti interessa davvero se il costruttore viene chiamato tramite un albero di espressioni, a patto che l'invocazione effettiva non richieda una riflessione. È possibile creare un metodo dinamico che inoltra a una chiamata del costruttore:
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++;
}
}
}
Nota che questo si comporta come una normale chiamata di metodo. x
non è impostato prima di stamparne il valore, quindi non viene reimpostato su 0
quando si richiama di nuovo il costruttore. A seconda di ciò che fa il costruttore, questo può o non può essere un problema.
Se si desidera utilizzare alberi di espressioni, utilizzare Expression.New . Ecco un esempio
var info = Expression.Parameter(typeof(SerializationInfo), "info");
var context = Expression.Parameter(typeof(StreamingContext), "context");
var callTheCtor = Expression.New(ctorInfo, info, context);
Questo non funziona su un oggetto esistente, ma dal momento che il tuo codice mostra GetUninitializedObject
, penso che potresti semplicemente rimuovere quella parte e usare Expression.New
per creare un nuovo oggetto.