Frage

Tut mir leid, wenn dies zuvor gefragt wurde. Ich habe eine ziemlich umfassende Suche durchgeführt und nichts Ähnliches gefunden, aber ich habe vielleicht etwas verpasst.

Und jetzt zur Frage: Ich versuche, einen Konstruktor durch Reflexion ohne Glück aufzurufen. Grundsätzlich habe ich ein Objekt, das ich klonen möchte, also suche ich den Kopierkonstruktor nach seinem Typ und möchte ihn dann aufrufen. Hier ist, was ich habe:

public Object clone(Object toClone) {
     MethodBase copyConstructor = type.GetConstructor(
         new Type[] { toClone.GetType() });
     return method.Invoke(toClone, new object[] { toClone }); //<-- doesn't work
}

Ich nenne die obige Methode wie so:

List<int> list = new List<int>(new int[] { 0, 1, 2 });
List<int> clone = (List<int>) clone(list);

Beachten Sie nun, dass die aufgerufene Methode, die ich verwende, ist MethodBase's aufrufen. ConstructorInfo Bietet eine Aufrufmethode, die funktioniert, wenn sie so aufgerufen wird:

return ((ConstructorInfo) method).Invoke(new object[] { toClone });

Ich möchte jedoch verwenden MethodBase's Methode, denn in der Realität, anstatt den Kopierkonstruktor jedes Mal nachzuschauen, wenn ich ihn in einem Wörterbuch speichere, und das Wörterbuch enthält sowohl Methoden als auch Konstrukteure, also ist es ein Dictionary<MethodBase>, nicht Dictionary<ConstructorInfo>. Ich konnte natürlich zu besetzen ConstructorInfo Wie oben, aber ich würde lieber das Casting vermeiden und das benutze MethodBase Methode direkt. Ich kann einfach nicht die richtigen Parameter herausfinden.

Irgendeine Hilfe? Vielen Dank.


BEARBEITEN

Benjamin,
Vielen Dank für Ihre Vorschläge. Ich habe tatsächlich genau das getan, was Sie in Ihrer zweiten Bearbeitung vorschlagen, außer (und das ist ein großes "außer") Mein Wörterbuch war wohin

class ClonerMethod {

    public MethodBase method;
    public bool isConstructor;

    ...

    public Object invoke(Object toClone) {
        return isConstructor ?
            ((ConstructorInfo) method).Invoke(new object[] { toClone }) : //<-- I wanted to avoid this cast
            method.Invoke(toClone, null);
    }

}

Und dann rief ich an ClonerMethod's invoke über das, was ich im Wörterbuch gefunden habe. Ich habe den Code nicht hinzugefügt. ConstructorInfo Verwendung MethodBase's Invoke Methode, also wollte ich keine unnötigen Informationen und zu viel Code für euch hinzufügen, um sie durchzulesen. Ich mag Ihre Verwendung von jedoch Func<,> Viel besser, also wechsle ich darauf. Auch das machen Clone Methode generic ist eine nette Ergänzung, aber in meinem Fall kennt der Anrufer die Art des Objekts nicht, also werde ich ihn stattdessen nicht generisch halten.

Ich wusste nichts davon Func<,>, Und wenn ich von dem Lambda -Operator wusste, hatte ich es vergessen (ich hatte so etwas vorher nicht wirklich gebraucht), also habe ich tatsächlich viel aus Ihrer Antwort gelernt. Ich liebe es immer, neue Dinge zu lernen, und das wird in Zukunft sehr praktisch sein. Vielen Dank! :)

War es hilfreich?

Lösung

Wenn Sie wissen, dass das Objekt einen solchen Konstruktor hat, haben Sie dann darüber nachgedacht, diese Überladung von zu verwenden Activator.CreateInstance stattdessen?


UPDATE: Sie haben also bereits eine Kaskadierungssuche nach MethodInfo/Methodbase und speichern sie -> Sie möchten/können nicht verwenden/können nicht verwenden Activator.

In diesem Fall sehe ich keinen Weg, was Sie wollen, ohne eine Besetzung. Aber - vielleicht könnten Sie die Architektur ändern, um a zu speichern Dictionary<Type, Func<object, object>> und fügen Sie diese hinzu Func<> Instanzen stattdessen. Macht den Anrufcode schöner (ich nehme an) und würde Ihnen erlauben, diese Besetzung einmal zu machen:

// Constructor
dictionary.Add(type,
  source => ((ConstructorInfo) method).Invoke(new object[] {source})
);

// Clone
dictionary.Add(type,
  source => method.Invoke(source, new object[]{})
);

Da Sie sich nur um den Unterschied zwischen Konstruktor und normaler Methode an der Website kümmern, auf der Sie sie greifen, würden Sie überhaupt keine Besetzung benötigen, oder?

// Constructor 2
dictionary.Add(type,
  source => yourConstructorInfo.Invoke(new object[] {source})
);

Wenn mir nicht etwas fehlt (natürlich möglich), könnte dies das Problem lösen, indem ich dies einmal auf der definierenden Seite des Zauns mache, und der Anrufer müsste nichts dagegen, wenn es sich um einen Konstruktor handelt oder nicht?


Ein letztes Mal, dann werde ich den Bearbeitungsspam stoppen. Ich war gelangweilt und hatte den folgenden Code. Versuchen Sie das zu erreichen?

public class Cloner {
    private readonly IDictionary<Type, Func<object, object>> _cloneMap =
            new Dictionary<Type, Func<object, object>>();

    public T Clone<T>(T source) {
        Type sourceType = source.GetType();
        Func<object, object> cloneFunc;

        if (_cloneMap.TryGetValue(sourceType, out cloneFunc)) {
            return (T)cloneFunc(source);
        }

        if (TryGetCopyConstructorCloneFunc(sourceType, out cloneFunc)) {
            _cloneMap.Add(sourceType, cloneFunc);
            return (T)cloneFunc(source);
        }

        if (TryGetICloneableCloneFunc(sourceType, out cloneFunc)) {
            _cloneMap.Add(sourceType, cloneFunc);
            return (T)cloneFunc(source);
        }

        return default(T);
    }

    private bool TryGetCopyConstructorCloneFunc(Type type, 
                    out Func<object, object> cloneFunc) {
        var constructor = type.GetConstructor(new[] { type });
        if (constructor == null) {
            cloneFunc = source => null;
            return false;
        }
        cloneFunc = source => constructor.Invoke(new[] { source });
        return true;
    }

    private bool TryGetICloneableCloneFunc(Type type,
                    out Func<object, object> cloneFunc) {
        bool isICloneable = typeof(ICloneable).IsAssignableFrom(type);
        var cloneMethod = type.GetMethod("Clone", new Type[] { });
        if (!isICloneable || (cloneMethod == null)) {
            cloneFunc = source => null;
            return false;
        }
        cloneFunc = source => cloneMethod.Invoke(source, new object[] {});
        return true;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top