Wie rufen Sie einen Konstruktor über einen Ausdrucksbaum auf einem vorhandenen Objekt auf?

c# expression-trees serialization

Frage

Ich versuche, den Deserialisierungskonstruktor für ein Objekt aufzurufen, das bereits existiert. Wie mache ich das mit Ausdrucksbäumen?

Ich habe es versucht:

// 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 });

aber Expression.Call akzeptiert nur MethodInfo nicht ConstructorInfo , so dass es nicht funktioniert - es sei denn, es gibt eine Möglichkeit, in eine MethodInfo zu konvertieren?

Aktualisieren

Ich habe einfach nur 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() });

Ich habe Angst vor der Leistung, aber es scheint der einzige Weg zu sein, dies zu tun.

Aktualisieren

Das hat jetzt eine richtige Antwort. Danke allen.

Akzeptierte Antwort

Wenn ich Ihre Frage richtig lese, interessiert es Sie nicht, ob der Konstruktor über einen Ausdrucksbaum aufgerufen wird, solange der eigentliche Aufruf keine Spiegelung benötigt. Sie können eine dynamische Methode erstellen, die an einen Konstruktoraufruf weiterleitet:

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++;
        }
    }   
}

Beachten Sie jedoch, dass sich dies wie ein normaler Methodenaufruf verhält. x wird nicht vor dem Drucken seines Werts festgelegt, sodass es beim erneuten Aufrufen des Konstruktors nicht auf 0 zurückgesetzt wird. Je nachdem, was Ihr Konstruktor tut, kann dies ein Problem sein oder auch nicht.


Beliebte Antwort

Wenn Sie Ausdrucksbäume verwenden möchten, verwenden Sie Expression.New . Hier ist ein Beispiel

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

var callTheCtor = Expression.New(ctorInfo, info, context);

Dies funktioniert nicht für ein vorhandenes Objekt, aber da Ihr Code GetUninitializedObject würde ich meinen, Sie könnten diesen Teil einfach entfernen und Expression.New , um ein neues Objekt zu erstellen.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum