O que é o equivalente a wildcards Java em C # genéricos
Pergunta
Estou desenvolvendo uma aplicação onde eu a necessidade de invocar um método de uma classe genérica e não se preocupam com a instâncias tipo real. Algo como o seguinte código Java:
public class Item<T>{
private T item;
public doSomething(){...}
}
...
public void processItems(Item<?>[] items){
for(Item<?> item : items)
item.doSomething();
}
Na época, eu estava em uma pressa, então eu resolvido meu problema, definindo uma interface com os métodos que eu precisava para invocar e fez a classe genérica implementá-lo.
public interface IItem
{
void doSomething();
}
public class Item<T> : IItem {
private T item;
public void doSomething(){...}
}
...
public void processItems(IItem[] items)
{
foreach(IItem item in items)
item.doSomething();
}
Esta solução funciona bem, mas eu gostaria de saber o que é a maneira correta para obter o mesmo comportamento.
EDIT:
Esqueci-me de referir que o chamador de processItems
não conhece os tipos reais. Na verdade, a idéia era que o array passado como argumento para processItems
poderia conter tipos misturados. Desde que não é possível ter uma matriz como em .Net, usando uma classe base genérico não ou interface parece ser o único caminho.
Solução
O caminho normal para fazer isso seria fazer o método genérico:
public void ProcessItems<T>(Item<T>[] items) {
foreach(Item<T> item in items)
item.DoSomething();
}
Assumindo que o chamador conhece o tipo, tipo de inferência deve significar que eles não têm que especificá-lo explicitamente. Por exemplo:
Item<int> items = new Item<int>(); // And then populate...
processor.ProcessItems(items);
Dito isto, criando uma interface não genérica especificando as operações do tipo agnóstico pode ser útil também. É muito vai depender do seu caso de uso exato.
Outras dicas
Eu vejo que você só deseja chamar algum método sem parâmetros ... já existe um contrato para que:. Action
public void processItems(IEnumerable<Action> actions)
{
foreach(Action t in actions)
t();
}
Cliente:
List<Animal> zoo = GetZoo();
List<Action> thingsToDo = new List<Action>();
//
thingsToDo.AddRange(zoo
.OfType<Elephant>()
.Select<Elephant, Action>(e => e.Trumpet));
thingsToDo.AddRange(zoo
.OfType<Lion>()
.Select<Lion, Action>(l => l.Roar));
thingsToDo.AddRange(zoo
.OfType<Monkey>()
.Select<Monkey, Action>(m => m.ThrowPoo));
//
processItems(thingsToDo);
Não há nenhuma maneira você pode omitir Tipo Parâmetros na implementação genérica .NET; isso é por design. Na verdade, isso só pode ser alcançado em Java por causa de sua implementação com base tipo-apagamento-.
Você só pode usar uma interface de base não-genérico (pense IEnumerable<T>
e IEnumerable
).
No seguimento pós de Jon. tornando o método genérico (um modelo) nega a exigência para esse tipo de funcionalidade (usando >). Você sempre pode alimentar um tipo em uma classe / função genérica e para os casos em que você não sabe que tipo você precisa, você pode fazer a ofender método / classe genérica, bem ... em última análise, o usuário tem que fornecer um tipo ao chamar tal função ou usar uma classe genérica, para o código para ser capaz de compilar ... caso contrário, você vai ter alguns erros do compilador.
Eu estive lutando com o mesmo problema quando se tratava de coisas portando a partir do Java onde eu tive construções como
if (o instanceof Collection<?>) doSoemthing((Collection<?>)o);
Felizmente acontece que um ICollection genérico é também um ICollection não-genéricos e se alguém precisa de tratar os elementos nele como objetos puros, ainda é possível:
if (o is ICollection) DoSomething((ICollection)o);
Dessa forma, uma vez que não se preocupam com o tipo real de elementos na coleção todos nós chegar aqui são objetos. Uma nota aqui:. Se a coleção estava segurando tipos primitivos (int ou bytes por exemplo), então autoboxing chutes no qual pode introduzir penalidade de desempenho