2009-02-25 24 views
5

que tienen una rutinaC# pasar una colección de objetos InterfaceImplementingClass a una rutina que lleva una colección de interfaz .NET objetos

public void SomeRoutine(List<IFormattable> list) { ... } 

entonces trato de llamar a esta rutina

List<Guid>list = new List<Guid>(); 
list.Add(Guid.NewGuid()); 
SomeRoutine(list); 

y falla con un error en tiempo de compilación. System.Guid implementa IFormattable, pero el error que consigo es

no se puede convertir de 'System.Collections.Generic.List' a 'System.Collections.Generic.List'

NOTA : Obtendrá el mismo error si solo usa una matriz de Guids. Los genéricos NO son la causa ...

¡Pero! Dada esta

public void SomeRoutine2(IFormattable obj) { ... } 

y esto

Guid a = Guid.NewGuid(); 
SomeRoutine2(a); 

Compila! Entonces la pregunta es ¿POR QUÉ? ¿Por qué puedo pasar un objeto Guid (que implementa IFormattable) a una rutina que acepta un objeto de IFormattable, pero cuando intento expandirlo a una colección (una lista genérica, una matriz o cualquier otra cosa), obtengo un error de conversión?

Me ha costado encontrar una respuesta, y pensé que sería el mejor lugar para ir.

+0

Te das cuenta de que la respuesta marcada (covarianza .NET 4.0) en realidad no se aplica a las listas, donde-como genéricos funciona * ahora *? –

+0

Vea también: http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html –

+0

Excepto que no funcionó con una matriz de Guids con una rutina que está buscando una matriz de IFormattable, ya sea ...? – emkayultra

Respuesta

8
+0

La covarianza de C# 4.0 no se aplica a clases concretas (solo a interfaces), y no se aplica a listas (solo IEnumerable - o cosas que son puramente "out") –

+0

¿Qué quiere decir exactamente cuando dices "pure out "¿?" – BFree

+0

Quiero decir, el 'IEnumerable ' y 'IEnumerator ' solo tienen un uso "fuera de servicio", es decir, "T Current {get;}". Marcar las cosas como "dentro" o "fuera" que permite la co/contra-varianza; pero solo funciona con uno ** o ** el otro. No puede tener el uso de "entrada" y "salida" (qué listas requerirían). –

2

Ha intentado declarar list como un objeto en lugar de una List<Guid>List<IFormattable>? Eso debería hacerte pasar el error de compilación.

+0

Los genéricos son una opción mucho mejor ... –

3

El problema es que una Lista <IFormattable> no es solo una colección de la que puede leer algunos IFormattables, también es una colección a la que puede agregar IFormattables. Una lista <Guid> cumple con el primer requisito, pero no el segundo. ¿Y si era SomeRoutine

public void SomeRoutine(List<IFormattable> list) 
{ 
    list.Add(5); 
} 

Eso es un Int32 IFormattable, por lo que el método debe ser capaz de añadir a la Lista de <IFormattable> que pidió. Eso no funcionará si el compilador le permite pasar su lista <Guid>.

Las nuevas características de C# 4.0 a las que se refiere BFree le permitirán decirle al compilador cuándo estas cosas son seguras.Puedes decir

  • Aunque pedí una referencia IFormattable, solo la leeré. Si realmente se trata de una referencia Guid, puedo tratarlo como IFormattable de forma segura, y no intentaré asignarle un Int32.
  • Aunque pedí una referencia IFormattable, solo le escribiré. Si realmente es una referencia de Objeto, entonces puedo asignarle de manera segura mi IFormattable, y no importa si el valor actual no es un IFormattable porque no lo leeré.
+0

C# 4.0 covarianza doesn ' t se aplica a clases concretas (solo a interfaces), y no se aplica a listas (solo IEnumerable - o cosas que son puramente "a cabo" –

+0

corrección; interfaces + delegates; pero genéricos sigue siendo una mejor respuesta aquí ... –

+0

Buen punto. Por supuesto, una Lista no puede hacer ninguna de las aserciones 'in' o 'out' que C 4.0 permitirá porque todavía necesita poder insertar y recuperar T, por lo que no es mejor. +1 para su respuesta de genéricos. – stevemegson

5

Este es un clásico caso de uso de genéricos; Proveedores:

public static void SomeRoutine<T>(IList<T> list) where T : IFormattable 
{ ... } 

Ahora dentro SomeRoutine tiene acceso a todos los miembros IFormattable, pero va a trabajar con:

List<Guid>list; ... 
SomeRoutine(list); // note no need to specify T 

Editar: Tengo, además, blogged en las diferencias entre 4.0 y covarianza para los genéricos este escenario

Cuestiones relacionadas