2010-07-09 18 views
23

Si tengo una clase genérica:colección de tipos genéricos

public class MyClass<T> 
{ 
    public T Value; 
} 

Quiero crear una instancia de varios elementos como ...

new MyClass<string> 
new MyClass<int> 

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

+0

¿Cómo se declara su clase? ¿Cómo ha definido la colección de respaldo? – Oded

+0

¿es esto simplemente un "hipotético", o algo que está tratando de usar como parte de una solución? Lo pido porque, dado un poco más de contexto, podríamos sugerir una mejor respuesta ... :) – Rob

+0

Más o menos las mismas preguntas se volvieron a publicar más tarde (Yo voté para cerrar, pero podría no hacerlo ya que es muy viejo): http: //stackoverflow.com/q/3777057/590790 –

Respuesta

23

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<T> : 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<T> objects. 
public class MyClassCollection 
{ 
    private Dictionary<Type, MyClass> _dictionary; 

    public MyClassCollection() 
    { 
     _dictionary = new Dictionary<Type, MyClass>(); 
    } 

    public void Put<T>(MyClass<T> item) 
    { 
     _dictionary[typeof(T)] = item; 
    } 

    public MyClass<T> Get<T>() 
    { 
     return _dictionary[typeof(T)] as MyClass<T>; 
    } 
} 
+1

Devolver el tipo es una variación interesante. –

+1

su solución es definitivamente elegante. Mantener un diccionario interno de tipos hace que los métodos de acceso sean más simples. –

+0

Además, ahora puede usar la palabra clave 'dynamic': http://msdn.microsoft.com/en-us/library/dd264736.aspx – Tommy

1

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

En otras palabras, si tuviera que añadir 2 elementos de una lista:

list.Add(new MyClass<string>()); 
list.Add(new MyClass<int>()); 

continuación, intenta hacer referencia a una:

var myItem = list[1]; 

El compilador no sabe lo genérico fue asignado a la lista MyClass en el elemento 1, porque los elementos se agregan en tiempo de ejecución, pero los genéricos se determinan en tiempo de compilación.

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


Si conoce el número de elementos de antemano, tal vez usted podría utilizar un Tuple?

7

La única manera que se me ocurre, la parte superior de la cabeza es el siguiente (envuelta en una aplicación de consola para la prueba):

class Program 
{ 
    static void Main(string[] args) 
    { 
     var x = new MyClass<string>() { Value = "34" }; 
     var y = new MyClass<int>() { Value = 3 }; 

     var list = new List<IMyClass>(); 
     list.Add(x); 
     list.Add(y); 

     foreach (var item in list) 
     { 
      Console.WriteLine(item.GetValue); 
     } 
    } 

    private interface IMyClass 
    { 
     object GetValue { get; } 
    } 

    private class MyClass<T> : IMyClass 
    { 
     public T Value; 

     public object GetValue 
     { 
      get 
      { 
       return Value; 
      } 
     } 
    } 
} 

es decir, tienen MiClase implementar una interfaz vacío y luego crear su colecciones como una que contiene instancias de clases que implementan esa interfaz.

Actualización: He añadido un método "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.

+0

buena llamada: esta es una respuesta equivalente a la mía utilizando una clase base :) –

+0

¿Pero cómo se accede a la propiedad de valor? –

+0

El problema es que quiere una propiedad de valor que termine teniendo el tipo correcto ya, que no se puede determinar. – CodingWithSpike

3

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

void Main() 
{ 
var a = new MyClass<string>(); 
var b = new MyClass<int>(); 
var c = new List<MyBase>(); 
c.Add(a); 
c.Add(b); 

} 

public class MyBase { } 
// Define other methods and classes here 
public class MyClass<T> : MyBase { 
public T Value { get; set;} 
} 
+1

Podría valer la pena, en su ejemplo, marcar MyBase como una clase abstracta para aclarar cuál es el propósito ... :) – Rob

+0

Interesante ... ¿Cómo puedo iterar por la lista y verificar los contenidos? de la propiedad Value? – Jeremy

+0

+1 de mí, más o menos la misma idea que mi respuesta :) – Rob

3

Usted quiere tener una colección de MiClase para los que el valor del parámetro de tipo T es diferente en cada caso. Esto no es posible en .NET; carece del equivalente del comodín de 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<T> : IMyClass { 
    public T Value { get; set; } 
    object IMyClass.Value { 
    get { return Value; } 
    set { Value = (T)value; } 
    } 
} 
List<IMyClass> m = new List<IMyClass> { new MyClass<string>(), new MyClass<int> }; 
+0

@ Commongenius, ¿has verificado que no compilará? Me compiló muy bien en VS2k8 – Rob

+0

Cuando publiqué mi comentario, la publicación original había sido modificada. Cambié mi comentario en consecuencia. –

3

tengo interfaces en la mayoría de mis tipos genéricos con los miembros "sin tipo":

private interface IMyClass 
{ 
    object UntypedValue { get; } 
} 

private class MyClass<T> : IMyClass 
{ 
    T Value { get; set; } 

    object UntypedValue { get { return Value; } } 
} 

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

+0

+1 para ofrecer valores escritos y no escritos. –

1

IList

No hay manera de definir una colección genérica que puede aceptar cualquier sabor de su clase genérica ... es decir IList<MyClass>. 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 compilación cada sabor de la clase genérica se traduce en un concreto. es decir, si tiene MyClass<string>, MyClass<int>, MyClass<bool>, el compilador 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<T> : IMyGeneric { 

    public MyGeneric() { 
     MyType = typeof(T); 
    } 

    public Type MyType { 
     get; set; 
    } 
} 

y entonces se puede decir

IList<IMyGeneric> list = new List<IMyGeneric>(); 
list.add(new MyGeneric<string>()); 
list.add(new MyGeneric<int>()); 
2

creo que el problema aquí es que las clases genéricas realmente no son del mismo tipo en absoluto. Simplemente son plantillas que crean tipos completamente nuevos en tiempo de compilación (si entiendo correctamente). Por lo tanto, MyClass<int> y MyClass<string> son completamente tipos diferentes, de acuerdo con el tiempo de ejecución. También podrían ser MyIntClass y MyStringClass, que obviamente no puedes tener en la misma lista sin boxearlos 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 commongenius' answer para una buena forma de hacerlo.

1

Desde .Net 3 ha habido una clase CompositeCollection que permite múltiples artículos únicos o incluso colecciones para ser contenidos dentro. 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í se muestra un ejemplo donde almaceno cosas diferentes de cadenas a decimales y el extracto y enumerar todos los artículos sobre artículos, luego 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<string>()) 
    Console.WriteLine ("\t" + str); 

/* Outputs: 

Every Item 
    1 
    Item One 
    1.0 
    1 
    Done 

Just Strings 
    Item One 
    Done 

*/