Question

Tout d'abord, désolé si cela a été demandé avant. Je l'ai fait une recherche assez complète et n'a rien trouvé tout à fait comme ça, mais je l'ai manqué quelque chose.

Et maintenant à la question: Je suis en train d'invoquer un constructeur par la réflexion, sans succès. Au fond, j'ai un objet que je veux cloner, donc je regarde le constructeur de copie pour son type et que vous souhaitez l'invoquer. Voici ce que j'ai:

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

J'appelle la méthode ci-dessus comme ceci:

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

Maintenant, remarquez la méthode Invoke J'utilise est le Invoke de MethodBase. ConstructorInfo fournit une méthode Invoke qui fonctionne si elle est invoquée comme ceci:

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

Cependant, je veux utiliser la méthode de MethodBase, parce qu'en réalité, au lieu de regarder le constructeur de copie chaque fois que je vais le stocker dans un dictionnaire, et le dictionnaire contient les méthodes et les constructeurs, il est donc un Dictionary<MethodBase>, pas Dictionary<ConstructorInfo>. Je pourrais en fonte de cours à ConstructorInfo comme je le fais ci-dessus, mais je préfère éviter la coulée et utiliser la méthode MethodBase directement. Je ne peux pas comprendre les bons paramètres.

Toute aide? Merci beaucoup.


EDIT
Benjamin,
Merci beaucoup pour vos suggestions. Je faisais en fait exactement ce que vous suggérez dans votre deuxième édition, à l'exception (et c'est un grand « sauf ») était mon dictionnaire où

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

}

Et puis j'ai appelé ClonerMethod de invoke sur ce que je trouve dans le dictionnaire. Je n'ai pas ajouté le code les traite de tout cela parce que la réponse que je cherchais était juste comment appeler Invoke sur un ConstructorInfo en utilisant la méthode MethodBase de Invoke, donc je ne voulais pas ajouter des informations inutiles et trop de code pour vous les gars à lire. Cependant, j'aime votre utilisation de Func<,> beaucoup mieux, donc je suis passer à cela. faisant également la méthode Clone générique est une belle addition, mais dans mon cas, l'appelant ne connaît pas le type de l'objet, donc je vais le garder à la place non générique.

Je ne savais pas Func<,>, et si je connaissais l'opérateur lambda j'avais oublié (je ne l'avais pas vraiment besoin de quelque chose comme ça avant), donc je l'ai fait beaucoup appris de votre réponse. J'aime toujours apprendre de nouvelles choses, et cela entrerai dans un avenir très pratique, donc merci beaucoup! :)

Était-ce utile?

La solution

Si vous savez que l'objet est d'avoir un constructeur comme ça, avez-vous pensé sur l'utilisation de cette surcharge de Activator.CreateInstance à la place?


Mise à jour:. Vous avez une recherche en cascade pour MethodInfo / MethodBase déjà et de les stocker -> Vous ne voulez pas / ne peut pas utiliser Activator

Dans ce cas, je ne vois pas une façon de faire ce que vous voulez sans un casting. Mais - vous pourriez peut-être changer l'architecture pour stocker un Dictionary<Type, Func<object, object>> et ajoutez les instances de Func<> à la place. Fait le plus joli de code d'appel (je suppose) et vous permettra de faire cette distribution une fois:

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

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

En fait, puisque vous ne vous préoccupez la différence entre le constructeur et la méthode normale à tout site où vous les prenez, vous ne seriez pas besoin d'un casting du tout, vous?

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

À moins que je me manque quelque chose (tout à fait possible, bien sûr) cela pourrait résoudre le problème en faisant cette fois sur le côté définissant de la clôture et l'appelant ne aurais pas besoin à l'esprit si ce constructeur est ou non?


Une dernière fois, je vais arrêter le spam modifier. Je me suis ennuyé et est venu avec le code suivant. Est-ce ce que vous essayez d'accomplir?

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;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top