c# - píldoras - programacion generica java
¿Cuál es el equivalente de comodines Java en genéricos C# (5)
Estoy desarrollando una aplicación en la que necesito invocar un método de una clase genérica y no me importan las instancias del tipo real. Algo así como el siguiente código de Java:
public class Item<T>{
private T item;
public doSomething(){...}
}
...
public void processItems(Item<?>[] items){
for(Item<?> item : items)
item.doSomething();
}
En ese momento tenía prisa, así que resolví mi problema definiendo una interfaz con los métodos que necesitaba invocar e hice que la clase genérica la implementara.
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 solución alternativa funciona bien, pero me gustaría saber cuál es la forma correcta de lograr el mismo comportamiento.
EDITAR:
Olvidé referirme que la persona que llama de processItems
no conoce los tipos reales. En realidad, la idea era que el conjunto pasado como argumento para processItems
podría contener tipos entremezclados. Como no es posible tener una matriz de este tipo en .Net, el uso de una clase base o interfaz no genérica parece ser la única forma.
Además de la publicación de Jon. hacer que el método genérico (una plantilla) anule el requisito para este tipo de funcionalidad (usando <?>). Siempre puede enviar un tipo a una clase / función genérica y, en los casos en que no sabe qué tipo necesitará, también puede hacer que el método / clase ofensivo sea genérico ... en última instancia, el usuario debe proporcionar un tipo al llamar tal función o usando una clase genérica, para que el código sea capaz de compilar ... de lo contrario, obtendrá algunos errores del compilador.
He estado luchando con el mismo problema cuando se trata de portar cosas desde Java donde he tenido construcciones como
if (o instanceof Collection<?>) doSoemthing((Collection<?>)o);
Afortunadamente, resulta que una ICollection genérica es también una colección IC no genérica y si alguien necesita tratar los elementos en ella como objetos puros, aún es posible:
if (o is ICollection) DoSomething((ICollection)o);
De esta forma, dado que no nos importa el tipo real de elementos en la colección, todos los objetos que obtenemos aquí. Una nota aquí: si la colección contenía tipos primitivos (int o byte, por ejemplo), entonces el autoboxing se inicia, lo que podría introducir una penalización en el rendimiento.
La forma normal de hacer esto sería hacer que el método sea genérico:
public void ProcessItems<T>(Item<T>[] items) {
foreach(Item<T> item in items)
item.DoSomething();
}
Suponiendo que la persona que llama conoce el tipo, la inferencia de tipo debería significar que no tienen que especificarlo explícitamente. Por ejemplo:
Item<int> items = new Item<int>(); // And then populate...
processor.ProcessItems(items);
Una vez dicho esto, la creación de una interfaz no genérica que especifique las operaciones tipognómicas también puede ser útil. Dependerá mucho de su caso de uso exacto.
No hay manera de que pueda omitir los parámetros de tipo en la implementación genérica de .NET; esto es por diseño. De hecho, esto solo se puede lograr en Java debido a su implementación basada en el borrado de tipos.
Solo puede usar una interfaz base no genérica (piense en IEnumerable<T>
e IEnumerable
).
Veo que solo quieres invocar algún método sin parámetros ... ya hay un contrato para eso: 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);