Pregunta

En primer lugar, lo siento si esto se ha preguntado antes. He hecho una búsqueda bastante completa y no he encontrado nada así, pero es posible que me haya perdido algo.

Y ahora a la pregunta: estoy tratando de invocar a un constructor a través de la reflexión, sin suerte. Básicamente, tengo un objeto que quiero clonar, así que busco el constructor de copias para su tipo y luego quiero invocarlo. Esto es lo que tengo:

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

Llamo al método anterior como así:

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

Ahora, observe que el método de Invoke que estoy usando es MethodBaseInvoca. ConstructorInfo Proporciona un método de invocación que funciona si se invoca así:

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

Sin embargo, quiero usar MethodBaseEl método del 's, porque en realidad en lugar de buscar el constructor de copias cada vez que lo almacenaré en un diccionario, y el diccionario contiene tanto métodos como constructores, por lo que es un Dictionary<MethodBase>, no Dictionary<ConstructorInfo>. Por supuesto que podría lanzar a ConstructorInfo Como hago arriba, pero prefiero evitar el casting y usar el MethodBase método directamente. Simplemente no puedo entender los parámetros correctos.

¿Alguna ayuda? Muchas gracias.


EDITAR

Benjamín,
Muchas gracias por tus sugerencias. De hecho, estaba haciendo exactamente lo que sugieres en tu segunda edición, excepto (y eso es un gran "excepto") mi diccionario era donde

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

}

Y luego llamé ClonerMethod's invoke Sobre lo que encontré en el diccionario. No agregué el código con todo eso porque la respuesta que estaba buscando era cómo llamar a Invoke en un ConstructorInfo usando MethodBase's Invoke Método, por lo que no quería agregar información innecesaria y demasiado código para que los leyeran. Sin embargo, me gusta tu uso de Func<,> mucho mejor, así que estoy cambiando a eso. También haciendo el Clone El método genérico es una buena adición, pero en mi caso la persona que llama no sabe el tipo de objeto, por lo que lo mantendré no genérico.

No sabía sobre Func<,>, y si supiera sobre el operador Lambda que había olvidado (realmente no había necesitado algo como esto antes), así que en realidad aprendí mucho de tu respuesta. Siempre me encanta aprender cosas nuevas, y esto será muy útil en el futuro, ¡así que muchas gracias! :)

¿Fue útil?

Solución

Si sabe que el objeto está teniendo un constructor como ese, ¿pensó en usar esta sobrecarga de Activator.CreateInstance ¿en cambio?


Actualización: por lo tanto, ya tiene una búsqueda en cascada de MethodInfo/Methodbase y almacenarlos -> No desea/no puede usar Activator.

En ese caso, no veo una manera de hacer lo que quieres sin un elenco. Pero, tal vez podrías cambiar la arquitectura para almacenar un Dictionary<Type, Func<object, object>> y agregar esos Func<> instancias en su lugar. Hace que el código de llamada sea más agradable (supongo) y le permitiría hacer este elenco una vez:

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

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

De hecho, dado que solo te importa la diferencia entre el constructor y el método normal en el mismo sitio donde los agarras, no necesitarías un elenco, ¿verdad?

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

A menos que me esté perdiendo algo (muy posible, por supuesto), esto podría resolver el problema haciendo esto una vez en el lado definitorio de la cerca y la persona que llama no tendrá que importarle si esto es constructor o no.


Una última vez, luego voy a detener el spam de edición. Estaba aburrido y se me ocurrió el siguiente código. ¿Es eso lo que estás tratando de lograr?

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;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top