Colección de tipos generics

Si tengo una clase genérica:

public class MyClass { public T Value; } 

Quiero crear instancias de varios elementos como …

 new MyClass new MyClass 

… y agrégalos a una colección. ¿Cómo defino la colección para que pueda contener una lista de tipos generics? Luego quiero iterar a través de la colección en algún punto y usar la propiedad Value. ¿Posible?

Haga que su clase genérica herede de una base no genérica o implemente una interfaz no genérica. Luego puede tener una colección de este tipo y emitirla dentro del código que use para acceder al contenido de la colección.

Aquí hay un ejemplo.

 public abstract class MyClass { public abstract Type Type { get; } } public class MyClass : MyClass { public override Type Type { get { return typeof(T); } } public T Value { get; set; } } // VERY basic illustration of how you might construct a collection // of MyClass objects. public class MyClassCollection { private Dictionary _dictionary; public MyClassCollection() { _dictionary = new Dictionary(); } public void Put(MyClass item) { _dictionary[typeof(T)] = item; } public MyClass Get() { return _dictionary[typeof(T)] as MyClass; } } 

La única forma en que puedo pensar, de la parte superior de mi cabeza es la siguiente (envuelto en una aplicación de la consola para la prueba):

 class Program { static void Main(string[] args) { var x = new MyClass() { Value = "34" }; var y = new MyClass() { Value = 3 }; var list = new List(); list.Add(x); list.Add(y); foreach (var item in list) { Console.WriteLine(item.GetValue); } } private interface IMyClass { object GetValue { get; } } private class MyClass : IMyClass { public T Value; public object GetValue { get { return Value; } } } } 

es decir, haga que MyClass implemente una interfaz vacía y luego cree sus colecciones como una que contenga instancias de clases que implementan esa interfaz.

Actualización: agregué un método de “GetValue” a la interfaz que le permite acceder al “Valor” de la instancia de MyClass como un Objeto. Esto es casi tan bueno como lo que va a obtener, afaik, si desea tener una colección que contenga tipos mixtos.

Querrá definir una clase base para MyClass, luego sus colecciones serán una lista de clase base. Ex:

 void Main() { var a = new MyClass(); var b = new MyClass(); var c = new List(); c.Add(a); c.Add(b); } public class MyBase { } // Define other methods and classes here public class MyClass : MyBase { public T Value { get; set;} } 

Tengo interfaces en la mayoría de mis tipos generics con miembros “Untyped”:

 private interface IMyClass { object UntypedValue { get; } } private class MyClass : IMyClass { T Value { get; set; } object UntypedValue { get { return Value; } } } 

También puede hacer esto mediante el uso de la implementación explícita de la interfaz, pero en mi opinión, es mucho más fácil al usar un nombre diferente. (Hay algunos consejos de CA sobre la implementación explícita de la interfaz también)

Desea tener una colección de MyClass para la cual el valor del parámetro de tipo T sea diferente en cada instancia. Esto no es posible en .NET; carece del equivalente del comodín Java (?). Lo que debe hacer en su lugar es crear una clase base o interfaz no genérica, que MyClass puede implementar. Por ejemplo:

 public interface IMyClass { object Value { get; set; } } public class MyClass : IMyClass { public T Value { get; set; } object IMyClass.Value { get { return Value; } set { Value = (T)value; } } } List m = new List { new MyClass(), new MyClass }; 

Creo que el problema aquí es que las clases genéricas realmente no son del mismo tipo. Simplemente son plantillas que crean tipos completamente nuevos en tiempo de comstackción (si entiendo correctamente). Por lo tanto, MyClass y MyClass son tipos completamente diferentes, de acuerdo con el tiempo de ejecución. También podrían ser MyIntClass y MyStringClass , que obviamente no puede tener en la misma lista sin boxear primero. No heredan (necesariamente) la misma clase base, implementan las mismas interfaces o cualquier otra cosa. Son tan diferentes como los otros dos tipos que existen, y debes tratarlos como tales (aunque creas que sabes mejor).

Por supuesto, puede hacer que implementen una interfaz, hereden un objeto base o cualquiera de las otras opciones ya dadas. Eche un vistazo a la respuesta de Commongenius para una buena manera de hacer esto.

Desde .Net 3 ha habido una clase CompositeCollection que permite contener múltiples elementos únicos o incluso colecciones. Es utilizado por los desarrolladores de WPF para almacenar y mostrar diferentes elementos en Xaml. Pero eso no significa que no se pueda usar en situaciones que no sean de WPF.

Aquí hay un ejemplo donde almaceno cosas diferentes de cadenas a decimales y extraigo y enumero sobre todos los artículos, luego artículos de un tipo específico:

 CompositeCollection cc = new CompositeCollection(); cc.Add(1); cc.Add("Item One"); cc.Add(1.0M); cc.Add(1.0); cc.Add("Done"); Console.WriteLine ("Every Item"); foreach (var element in cc) Console.WriteLine ("\t" + element.ToString()); Console.WriteLine (Environment.NewLine + "Just Strings"); foreach (var str in cc.OfType()) Console.WriteLine ("\t" + str); /* Outputs: Every Item 1 Item One 1.0 1 Done Just Strings Item One Done */ 

Creo que su colección tendría que ser del mismo tipo MyClass (como en T tendría que ser el mismo), porque el comstackdor no sabrá qué tipos ha agregado a qué elementos de la colección.

En otras palabras, si tuviera que agregar 2 elementos a una lista:

 list.Add(new MyClass()); list.Add(new MyClass()); 

luego intenta hacer referencia a uno:

 var myItem = list[1]; 

El comstackdor no sabe qué genérico se asignó a la lista MyClass en el elemento 1 , porque los elementos se agregan en tiempo de ejecución, pero los generics se determinan en tiempo de comstackción.

Estoy bastante seguro de que lo que quieres hacer no se puede hacer.


Si conoce la cantidad de elementos por adelantado, ¿quizás podría usar un Tuple ?

IList

No hay forma de definir una colección genérica que pueda aceptar cualquier sabor de su clase genérica … es decir, IList . Las clases genéricas son solo un atajo para que el desarrollador ahorre en la escritura de un montón de código repetitivo, pero en el momento de la comstackción cada sabor de la clase genérica se traduce en un concreto. es decir, si tiene MyClass , MyClass , MyClass entonces el comstackdor generará 3 clases separadas y distintas. La única forma de hacer lo que desea es tener una interfaz para su genérico.

 public interface IMyGeneric { Type MyType { get; set;} } class MyGeneric : IMyGeneric { public MyGeneric() { MyType = typeof(T); } public Type MyType { get; set; } } 

y luego puedes decir

 IList list = new List(); list.add(new MyGeneric()); list.add(new MyGeneric());