2010-11-25 17 views
6

Con el siguiente código, recibo un error de compilación "no se puede generar implícitamente" en los "libros de devolución"; línea.¿Cómo devuelvo correctamente un tipo de Lista de interfaces?

Pensé que porque estoy devolviendo una lista de objetos del libro que implementan IPublication esto debería funcionar bien?

public interface IPublication {} 

public class Book : IPublication {} 

public List<IPublication> GetBooks() 
{ 
    List<Book> books = new List<Book>(); 
    return books; 
} 

Observo que si devuelvo un solo libro como un solo objeto IPublication, funciona bien. Al presentar el List<> se requiere un lanzamiento explícito.

Como solución que estoy utilizando:

return books.Cast<IPublication>().ToList(); 

Respuesta

7

El problema es que un List<IPublication> es algo que puede contener cualquier clase que herede de IPublication. Dado que el compilador no sabe que no se trata de poner un Magazine en el resultado de GetBooks(), usted tiene que escribir su función como esta:

public List<IPublication> GetBooks() 
{ 
    List<IPublication> books = new List<IPublication>(); 
    // put only books in here 
    return books; 
} 

Si su función devuelve una lista inmutable que no lo hace tiene que ser visitada por el índice (y ya está en C# 4), se puede escribir así:

public IEnumerable<IPublication> GetBooks()   
{   
    List<Book> books = new List<Book>();   
    return books;   
} 

Si puede devolver un IEnumerable<T> pero estás usando C# 3, puede hacer lo cdhowie sugiere :

public IEnumerable<IPublication> GetBooks()   
{   
    List<Book> books = new List<Book>();   
    return books.Cast<IPublication>();   
} 

Si usa C# 2, es mejor usar el primer método que propuse.

+1

En tu último ejemplo, también podrías' devolver libros.Cast < IPublication>(); 'en C# 3 e inferior. No duplicará la lista, sino que devolverá un enumerable que arrojará cada elemento al tipo de interfaz uno por uno, sin requerir casi ninguna memoria para hacerlo. – cdhowie

+0

Buen punto, cdhowie. Sin embargo, solo funciona en C# 3 y superior. La versión 2 no tenía métodos de extensión o una función 'Cast'. Por supuesto, puede importar la clase 'Enumerable' y llamarla estáticamente, pero probablemente no valga la pena el esfuerzo. – Gabe

0

Creo que se está trabajando en las versiones antiguas en lugar de C# 4.0, donde tenemos covariance and contravariance. Así que solo puedes convertir el elemento de lista por elemento.

+1

Dado que 'List ' no es una interfaz, la varianza no lo ayudará aquí. Incluso si cambia a una interfaz, todavía no lo ayudará, porque 'IList ' es una interfaz invariable ('T' es tanto una entrada como una salida de sus funciones). – Gabe

+0

@Gabe: lo siento, no leí la pregunta con cuidado, realmente parece una pregunta de varianza (ntra). Pero aquí tengo otra pregunta: creo que el código de OP es correcto usando 'return books.Cast (). ToList()', ¿cuál es su pregunta? –

+0

El problema con 'books.Cast (). ToList()' es que crea una lista duplicada completamente nueva. Él quiere saber cómo evitar tener que crear esa copia. – Gabe

2

No es una conversión segura. Imagina si alguien pone una revista en tu lista de libros.

public class Library 
{ 
    public List<Book> books = new List<Book>(); 
    public List<IPublication> GetBooks() 
    { 
     return books; 
    } 
} 

Por otra parte,

Magazine mag = ...; 
List<IPublication> pubs = someLibrary.GetBooks() 
pubs.Add(mag); 

En .NET 4, se puede devolver un IEnumerable<IPublication>, tal como se describe here, sin crear nuevos objetos. cdhowie también ofrece una buena solución para versiones más bajas. Simplemente suelte .ToList(), y devuelva IEnumerable.

Me doy cuenta de que su caso particular es seguro. Pero el compilador no puede verificar eso.

1

Usted tiene que usar su solución menos que esté en .NET 4.

Considere si alguien fuera a hacer esto:

public class Magazine : IPublication { } 

// Somewhere... 

var foo = something.GetBooks(); 
foo.Add(new Magazine()); 

Si el tiempo de ejecución permite la conversión de List<Book> a List<IPublication>, a continuación, ¡se le permitiría agregar revistas a una lista de libros! Esto rompe el sistema de tipo, porque si algo todavía tuviera una referencia al List<Book>, con razón no esperaría un Magazine en él, trataría el Magazine como si fuera un libro, y luego se bloquea el tiempo de ejecución (o peor - corrupción de datos o cosas inseguras).

Cuestiones relacionadas