Chamando a invoque do MethodBase em um construtor (reflexão)
-
23-09-2019 - |
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 é MethodBase
está 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 MethodBase
o 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! :)
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;
}
}