2009-02-26 24 views
15

y gracias por cualquier ayuda.return unknown Generic List <T>

¿Cómo regresaría de un método desconocido tipo Generic.List?

public void Main() 
{ 
    List<A> a= GetData("A"); 
} 

public List<T> GetData(string listType) 
{ 
    if(listType == "A") 
    { 
    List<A> a= new List<A>() 
    ... 
    return a; 
    } 
    else 
    { 
    List<B> b = new List<B>() 
    return b; 

    } 
} 

En el siguiente ejemplo que recibo un error similar: no puede convertir a List<A>List<T>

Es esto posible? El error ocurre en 'return a;' línea de código.
Además, ¿Qué necesito hacer para asegurarse de que un error no se produce en la línea:

List<A> a= GetData("A"); 

Gracias, Steven

Respuesta

20

Uso IList en lugar de List<T>.

+0

De acuerdo que ese debería ser el objetivo final, pero realmente no lo ayuda a resolver el problema inmediato. –

4

EDITAR por la respuesta de Orion a continuación, añadió contraint que AnthonyWJones sugirieron

probablemente debería tener una clase de interfaz/abstracto que A y B están heredando de

public interface IMyInterface { } 
    public class A : IMyInterface { } 
    public class B : IMyInterface { } 

    public List<IMyInterface> GetData<T>() where T : IMyInterface 
    { 
     List<IMyInterface> myList = new List<IMyInterface>(); 
     if (typeof(T) == typeof(A)) 
     { 
      myList.Add(new A()); 
     } 
     if (typeof(T) == typeof(B)) 
     { 
      myList.Add(new B()); 
     } 
     return myList; 
    } 
+0

Olvidó el parámetro de tipo, lo agregó por usted. –

+0

if (typeof (T) == typeof (A)) hace panda triste :-( –

+0

@Orion ¿Cómo podría mejorarlo? Estoy de acuerdo, parece feo, pero mi idea se cumplió. –

8

No se puede directamente devolver un List<T> Me gusta esto.

¿Por qué? Básicamente porque List<A> y List<B> (o List<string> frente a List<int> que es lo mismo) se consideran como 2 clases independientes no relacionadas.
Del mismo modo que no puede devolver string desde una función declarada para devolver int, no puede devolver una Lista de cadenas de una función que se declara para devolver una lista de entradas. El <T> aquí es un poco de arenque rojo. No se pudo escribir un método genérico que devolvió cadenas y valores, ya sea ...

Consulte here para obtener más información sobre ese tipo de cosas.

Así que lo que tiene que hacer es devolver algo que ambos tipos se derivan de (lo que "tienen en común".)
Como John Rasch says, usted podría volver IList, (nótese el genérico, por lo que es sólo una lista de object s) o simplemente devolverlo como object. Lamentablemente, no hay forma de preservar el tipo de la lista.

1

Si no sabe el tipo que desea hasta el tiempo de ejecución, entonces los genéricos son probablemente la herramienta incorrecta para el trabajo.

Si su función cambia significativamente el comportamiento (como cambiar el tipo de devolución) en función de un argumento, probablemente debería ser de dos funciones.

Parece que esta función no debe ser genérica, y en realidad debe ser de dos funciones.

public void Main() { 
    List<A> a = GetDataA(); 
} 

public List<A> GetDataA() { 
    List<A> a= new List<A>() 
    ... 
    return a; 
} 
public List<B> GetDataB() { 
    List<B> b= new List<B>() 
    ... 
    return b; 
} 
10

Una alternativa a limitarse a devolver una lista de objetos sería o bien asegurarse de que A y B derivan de un tipo base común o implementar una interfaz común, entonces devolver una lista de ese tipo base o interfaz.Incluir una restricción sobre el método genérico a tal efecto: -

List<ICommon> GetData<T>() where T: ICommon 
{ 

} 
+0

ingenioso, no sabía sobre la restricción 'donde T: ICommon' +1 –

7

A menos que haya una razón específica que no se puede especificar el tipo real antes de tiempo, sólo puede hacer que el propio método genérico:

public void Main() { 
    List<A> a = GetData<A>(); 
} 

public List<TType> GetData<TType>() { 
    List<TType> list= new List<TType>(); 
    ... 
    return list; 
} 
2

Tuve que resolver un problema similar recientemente donde ninguna de las soluciones propuestas fue satisfactoria; restringir el parámetro de tipo no era práctico. En su lugar, I permite a los consumidores del método decidir cómo administrar los datos. Por ejemplo, puede escribir una versión genérica de String.Split() que devuelva una lista fuertemente tipada, siempre y cuando le diga cómo convertir subcadenas en T.

Una vez que esté dispuesto a pasar la responsabilidad por la pila de llamadas (y sentirse cómodo pasando las lambdas), puede generalizar este patrón arbitrariamente. Por ejemplo, si la forma en que GetData() varía (como aparentemente asumen algunas respuestas), también puede elevar esa función al alcance de la persona que llama.

Demostración:

static void Main(string[] args) 
{ 
    var parseMe = "Hello world! 1, 2, 3, DEADBEEF"; 

    // Don't need to write a fully generic Process() method just to parse strings -- you could 
    // combine the Split & Convert into one method and eliminate 2/3 of the type parameters 
    List<string> sentences = parseMe.Split('!', str => str); 
    List<int> numbers = sentences[1].Split(',', str => Int32.Parse(str, NumberStyles.AllowHexSpecifier | NumberStyles.AllowLeadingWhite)); 

    // Something a little more interesting 
    var lettersPerSentence = Process(sentences, 
            sList => from s in sList select s.ToCharArray(), 
            chars => chars.Count(c => Char.IsLetter(c))); 
} 

static List<T> Split<T>(this string str, char separator, Func<string, T> Convert) 
{  
    return Process(str, s => s.Split(separator), Convert).ToList(); 
} 

static IEnumerable<TOutput> Process<TInput, TData, TOutput>(TInput input, Func<TInput, IEnumerable<TData>> GetData, Func<TData, TOutput> Convert) 
{ 
    return from datum in GetData(input) 
      select Convert(datum); 
} 

gurús de programación funcional probablemente bostezo en esta exploración: "que sólo estás componiendo un mapa un par de veces" Incluso los chicos de C++ podrían afirmar que es un ejemplo en el que las técnicas de plantilla (es decir, STL transform() + functors) requieren menos trabajo que los genéricos. Pero como alguien que principalmente hace C#, fue agradable encontrar una solución que preservara tanto la seguridad del tipo como el uso del lenguaje idiomático.

0

Ya llegué demasiado tarde pero vine aquí con el mismo problema y así es como lo resolví usando interfaces. Pensé en publicarlo en beneficio de los demás

public interface IEntity 
    { 
     int ID 
     { 
      get; 
      set; 
     } 
    } 

public class Entity2:IEntity 
    { 
     public string Property2; 

     public int ID 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 
    } 

De forma similar para Entity1.

Ahora en mi clase (mi capa de negocio) Tengo este método

public List<IEntity> GetEntities(Common.EntityType entityType) 
      { 
       List<IEntity> entities = new List<IEntity>(); 

       switch (entityType) 
       { 
        case Common.EntityType.Accounts: 
         Entity1 entity1 = new Entity1(); 
         entity1.Property1 = "AA"; 
         entities.Add(entity1); 

         break; 
        case Common.EntityType.Brands: 
         Entity2 entity2 = new Entity2(); 
         entity2.Property2 = "AA"; 
         entities.Add(entity2); 

         break; 
        default: 
         break; 
       } 

return entities; 
     } 

Desde la interfaz de usuario, yo diría que es como esto

BusinessClass b = new BusinessClass(); 
     List<IEntity> a = b.GetEntities(Common.EntityType.Accounts); 

espero que esto ayude

0

Una solución es encapsular los datos en un contenedor que funcionará como un cliente en el Visitor Pattern.

En primer lugar alguna de las interfaces que coincida con el patrón:

/// <summary> 
/// The Client 
/// </summary> 
interface IDataContainer 
{ 
    void AcceptDataProcessor(IDataProcessor dataProcessor); 
} 

/// <summary> 
/// The Visitor. 
/// </summary> 
interface IDataProcessor 
{ 
    void WorkOn<TData>(List<TData> data); 
} 

A continuación, una implementación de cada uno:

class DataContainer<TData> : IDataContainer 
{ 
    readonly List<TData> list; 

    public DataContainer(List<TData> list) 
    { 
     this.list = list; 
    } 

    public void AcceptDataProcessor(IDataProcessor dataProcessor) 
    { 
     dataProcessor.WorkOn(list); // Here the type is known. 
    } 
} 

class PrintDataProcessor : IDataProcessor 
{ 
    public void WorkOn<TData>(List<TData> data) 
    { 
     // print typed data. 
    } 
} 

A continuación, un uso de ella:

public void Main() 
{ 
    var aContainer = GetData("A"); 
    var bContainer = GetData("B"); 

    var printProccessor = new PrintDataProcessor(); 

    aContainer.AcceptDataProcessor(printProccessor); // Will print A data 
    bContainer.AcceptDataProcessor(printProccessor); // Will print B data 
} 


public IDataContainer GetData(string listType) 
{ 
    if (listType == "A") 
     return new DataContainer<A>(new List<A>()); 
    if (listType == "B") 
     return new DataContainer<B>(new List<B>()); 
    throw new InvalidOperationException(); 
} 

La idea es que DataContainer saben el tipo subyacente pero no lo expone.

  • No exponga de manera GetData lata contiene cualquier tipo de datos (pero está hided).
  • DataContainer conocer el tipo subyacente, que se encarga de llamar al buen método mecanografiada del trabajador: dataProcessor.WorkOn(list);

Es un patrón de gran alcance, pero cuestan mucho en términos de código.