Pergunta

Primeiro de tudo, desculpe se isso já foi perguntado antes. Eu fiz uma pesquisa bastante abrangente e não encontrei nada parecido, mas posso ter perdido alguma coisa.

E agora para a pergunta: estou tentando invocar um construtor através da reflexão, sem sorte. Basicamente, tenho um objeto que quero clonar, então procuro o construtor de cópias para o seu tipo e depois quero invocá -lo. Aqui está o que eu tenho:

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

Eu chamo o método acima como assim:

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

Agora, observe o método de invocado que estou usando é MethodBaseestá invocando. ConstructorInfo Fornece um método de invocar que funciona se invocado assim:

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

No entanto, eu quero usar MethodBaseo método, porque, na realidade, em vez de procurar o construtor de cópias toda vez que o armazenarei em um dicionário, e o dicionário contém métodos e construtores Dictionary<MethodBase>, não Dictionary<ConstructorInfo>. Eu poderia ter sido lançado para ConstructorInfo Como faço acima, mas prefiro evitar o elenco e usar o MethodBase método diretamente. Eu simplesmente não consigo descobrir os parâmetros certos.

Qualquer ajuda? Muito obrigado.


EDITAR

Benjamin,
Muito obrigado por suas sugestões. Na verdade, eu estava fazendo exatamente o que você sugere em sua segunda edição, exceto (e isso é um grande "exceto") meu dicionário foi onde

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

}

E então eu liguei ClonerMethod's invoke Sobre o que encontrei no dicionário. Não adicionei o código as acordos com tudo isso, porque a resposta que eu estava procurando era apenas como chamar Invoke em um ConstructorInfo usando MethodBase's Invoke Método, então eu não queria adicionar informações desnecessárias e muito código para vocês lerem. No entanto, eu gosto do seu uso de Func<,> Muito melhor, então estou mudando para isso. Também fazendo o Clone Método genérico é uma boa adição, mas no meu caso o chamador não sabe o tipo de objeto, então o manterei não genérico.

Eu não sabia sobre Func<,>, e se eu soubesse do operador Lambda que havia esquecido (eu realmente não precisava de algo assim antes), então eu realmente aprendi muito com a sua resposta. Eu sempre amo aprender coisas novas, e isso será muito útil no futuro, então muito obrigado! :)

Foi útil?

Solução

Se você sabe que o objeto está tendo um construtor como esse, você pensou em usar essa sobrecarga de Activator.CreateInstance em vez de?


ATUALIZAÇÃO: Então você já tem uma pesquisa em cascata por MethodInfo/MethodBase e os armazena -> Você não quer/não pode usar Activator.

Nesse caso, não vejo uma maneira de fazer o que você quer sem um elenco. Mas - talvez você possa mudar a arquitetura para armazenar um Dictionary<Type, Func<object, object>> e adicione isso Func<> instâncias em vez disso. Torna o código mais agradável (presumo) e permitiria que você faça este elenco uma vez:

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

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

De fato, como você se preocupa apenas com a diferença entre o construtor e o método normal no local em que você os pega, não precisaria de um elenco, seria?

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

A menos que eu esteja perdendo alguma coisa (bem possível, é claro), isso pode resolver o problema fazendo isso uma vez no lado definidor da cerca e o chamador não precisaria se importar se isso for construtor ou não?


Uma última vez, então vou parar o spam de edição. Fiquei entediado e criei o código a seguir. É isso que você está tentando realizar?

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top