2009-02-26 25 views
23

Tener la siguiente clase genérica que contendría ya sea string, int, float, long como el tipo:C#: Declarar y usar una lista de clases genéricas con diferentes tipos, ¿cómo?

public class MyData<T> 
{ 
    private T _data; 

    public MyData (T value) 
    { 
     _data = value; 
    } 

    public T Data { get { return _data; } } 
} 

Estoy tratando de obtener una lista de MyData<T>, donde cada elemento sería diferente de T.

Quiero ser capaz de acceder a un elemento de la lista y obtener su valor como en el siguiente código:

MyData<> myData = _myList[0]; // Could be <string>, <int>, ... 
SomeMethod (myData.Data); 

donde SomeMethod() se declara como sigue:

public void SomeMethod (string value); 
public void SomeMethod (int value); 
public void SomeMethod (float value); 

ACTUALIZACIÓN:

SomeMethod() es de otra clase de nivel que no tengo control y SomeMethod(object) no existe.


Sin embargo, parece que no puedo encontrar la manera de hacer feliz al compilador.

¿Alguna sugerencia?

Gracias.

+0

Podría explicar lo que hace su SomeMethod? Es posible que podamos ayudarlo mejor. –

+0

Lo que SomeMethod hace no es relevante aquí. Añadiré que no hay definición para SomeMethod (objeto). –

+0

Su diseño básico es defectuoso. Te sugiero que hagas una pregunta donde detallas los requisitos y tu solución y pides sugerencias. – Will

Respuesta

8

Delegados realmente puede ayudar a simplificar esto, y aún así mantener las cosas con seguridad de tipos:

public void TestMethod1() 
{ 
    Action<SomeClass, int> intInvoke = (o, data) => o.SomeMethod(data); 
    Action<SomeClass, string> stringInvoke = (o, data) => o.SomeMethod(data); 

    var list = new List<MyData> 
    { 
     new MyData<int> { Data = 10, OnTypedInvoke = intInvoke }, 
     new MyData<string> { Data = "abc", OnTypedInvoke = stringInvoke } 
    }; 

    var someClass = new SomeClass(); 
    foreach (var item in list) 
    { 
     item.OnInvoke(someClass); 
    } 
} 

public abstract class MyData 
{ 
    public Action<SomeClass> OnInvoke; 
} 

public class MyData<T> : MyData 
{ 
    public T Data { get; set; } 
    public Action<SomeClass, T> OnTypedInvoke 
    { set { OnInvoke = (o) => { value(o, Data); }; } } 
} 

public class SomeClass 
{ 
    public void SomeMethod(string data) 
    { 
     Console.WriteLine("string: {0}", data); 
    } 

    public void SomeMethod(int data) 
    { 
     Console.WriteLine("int: {0}", data); 
    } 
} 
+0

Excelente revelador. – Telemat

0

Heredar MyData<T> de una clase no genérica MyData y hacer una lista de eso.

De esta manera, no puede resolver automáticamente la sobrecarga. Tienes que hacerlo de forma manual.

abstract class MyData { 
    protected abstract object GetData(); 
    protected abstract Type GetDataType(); 
    public object Data { 
     get { return GetData(); } 
    } 
    public Type DataType { 
     get { return GetDataType(); } 
    } 
} 

class MyData<T> : MyData { 
    protected override object GetData() { return Data; } 
    protected override Type GetDataType() { return typeof(T); } 
    public new T Data { 
    get { ... } 
    } 
} 
+0

Ok, ahora lo usaría como: Lista list = new List (); list.Add (new MyData ()); list.Add (new MyData ()); SomeMethod (list [0] .Data); De alguna manera, necesitaré que SomeMethod() acepte un objeto. Esto no es lo que necesito desafortunadamente. Gracias por responder. Stecy –

+0

Sí, es por eso que le dije que debe administrar manualmente las sobrecargas teniendo en cuenta el tipo. No hay una manera directa respaldada por los genéricos. –

1

En ese caso deberá MyData<object> ya que es el único que esos tipos tienen en común.

+2

¿Cómo permite MyData la llamada de SomeMethod (int)? –

0

genéricos permiten especificar un tipo para toda la lista cuando se crea la lista, por ejemplo, una lista para almacenar int se crearía como esto

var myData = new MyData<int>(); 

Si desea almacenar múltiples tipos de la misma lista genérica puede especificar un tipo base común o interfaz para esos tipos. Desafortunadamente, en su caso, el único tipo de base común para los tipos que desea almacenar sería un objeto.

var myData = new MyData<object>(); 

Pero solo puede utilizar la lista no genérica para almacenar objetos.

6

Simplemente use una ArrayList y olvide el tipo MyData<T>.

ArrayList myStuff = getStuff(); 
float x = myStuff.OfType<float>().First(); 
SomeMethod(x); 
string s = myStuff.OfType<string>().First(); 
SomeMethod(s); 

El problema con MyData<T> es que usted está esperando el compilador para comprobar un tipo que sólo se conoce en tiempo de ejecución. Los compiladores comprueban los tipos que se conocen en tiempo de compilación.

+0

¡Gracias! Esta fue la respuesta a mi problema personal. Intentaba hacer un seguimiento de una lista de BlockingCollection , y luego verificar a través de un GetQueue donde T: Método de interfaz si una cola de tipo T ya existía o no en mi lista, y si no, crear una instancia y devolver la nueva cola vacía. Pero BlockingCollection no se puede convertir implícitamente en BlockingCollection , incluso pensó T: Interfaz, y no pude encontrar la implementación covariante/contravariante/delegada correcta para hacer esto – Isaac

3

No puede hacerlo de la manera que desee.

Cuando se inicializa una instancia de una clase genérica, está vinculada a un tipo particular. Como desea mantener objetos de diferentes tipos en su lista, debe crear una instancia vinculada al mínimo común denominador, en su caso es Object.

Sin embargo, eso significa que la propiedad Datos ahora devolverá un objeto de tipo Objeto. El compilador no puede inferir el tipo de datos real en tiempo de compilación, por lo que puede elegir la sobrecarga SomeMethod adecuada.

Debe proporcionar una sobrecarga de SomeMethod que toma Object como parámetro, o eliminar el requisito de mantener diferentes tipos diferentes en su colección.

O puede ir con una colección estándar IEnumerable (como Array) y usar el método de extensión OfType<> para obtener el subconjunto de la colección de un tipo particular.

+0

Temía que se resolviera a usar un objeto. .. Sin embargo, no puedo usar un objeto porque SomeMethod es de una biblioteca y no puedo cambiar el hecho de que no aceptará un objeto ... Además, no quiero cambiar () sobre el tipo de la variable ... –

1

Comodines sugeridos hace un tiempo here. Cerrado como "no se solucionará" :(

13

Creo que el problema es que estás tratando de crear un tipo genérico y luego creas una lista de ese tipo genérico. Podrías lograr lo que Trataremos de subcontratar los tipos de datos que intentas admitir, digamos como un elemento de IData, y luego crearemos tu MyData genérico con una restricción de IData. La desventaja de esto sería que tendrías que crear tu propio tipos de datos que representan todos los tipos de datos primitivos que está utilizando (cadena, int, float, long) podría ser algo como esto:.

public class MyData<T, C> 
    where T : IData<C> 
{ 
    public T Data { get; private set; } 

    public MyData (T value) 
    { 
     Data = value; 
    } 
} 

public interface IData<T> 
{ 
    T Data { get; set; } 
    void SomeMethod(); 
} 

//you'll need one of these for each data type you wish to support 
public class MyString: IData<string> 
{ 
    public MyString(String value) 
    { 
     Data = value; 
    } 

    public void SomeMethod() 
    { 
     //code here that uses _data... 
     Console.WriteLine(Data); 
    } 

    public string Data { get; set; } 
} 

y entonces estás aplicación sería algo así como:

var myData = new MyData<MyString, string>(new MyString("new string"));  
// Could be MyString, MyInt, ... 
myData.Data.SomeMethod(); 

es un poco más de trabajo, pero obtienes la funcionalidad que estabas buscando.

ACTUALIZACIÓN: quitar SomeMethod de su interfaz y simplemente hacer esto

SomeMethod(myData.Data.Data); 
+0

Lástima que tenga que envolver los tipos primitivos, pero eso es algo que estoy dispuesto a aceptar, ya que no hay otra forma que parezca. Sin embargo, en su ejemplo, asumió que SomeMethod() era parte de MyData, que no es el caso (he actualizado la pregunta). –

+0

en realidad puede lograr lo mismo, simplemente lo envolví para que estuviera más encapsulado, consulte mi actualización – Joseph

+0

Entonces ... ¿cómo coloca uno MyData y MyData en la misma colección? –

1

Puede crear un contenedor genérico para SomeMethod y comprobar si el tipo del argumento genérico, a continuación, delegar en el método apropiado.

public void SomeMethod<T>(T value) 
{ 
    Type type = typeof(T); 

    if (type == typeof(int)) 
    { 
     SomeMethod((int) (object) value); // sadly we must box it... 
    } 
    else if (type == typeof(float)) 
    { 
     SomeMethod((float) (object) value); 
    } 
    else if (type == typeof(string)) 
    { 
     SomeMethod((string) (object) value); 
    } 
    else 
    { 
     throw new NotSupportedException(
      "SomeMethod is not supported for objects of type " + type); 
    } 
} 
+0

Gracias por su respuesta. Me temo que tendré que usar el boxeo/unboxing después de todo. Pensé que debe haber una manera más eficiente. –

Cuestiones relacionadas