Tengo una private readonly
lista de LinkLabel
s ( IList<LinkLabel>
). Luego agrego LinkLabel
s a esta lista y agrego esas etiquetas a un me FlowLayoutPanel
gusta de la siguiente manera:
foreach(var s in strings)
{
_list.Add(new LinkLabel{Text=s});
}
flPanel.Controls.AddRange(_list.ToArray());
ReSharper me muestra una advertencia: Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
Por favor, ayúdame a descubrir:
- ¿Qué significa esto?
- Este es un control de usuario y no se accederá a varios objetos para configurar etiquetas, por lo que mantener el código como tal no lo afectará.
fuente
LinkLabel
(tipo especializado) aControl
(tipo base).LinkLabel[]
aControl[]
, que todavía es legal, pero puede tener un problema de tiempo de ejecución. Todo lo que ha cambiado es la forma en que se hace referencia a la matriz. La matriz en sí no cambia. ¿Ves el problema? La matriz sigue siendo una matriz del tipo derivado. La referencia es a través de una matriz del tipo base. Por lo tanto, es tiempo de compilación legal para asignarle un elemento del tipo base. Sin embargo, el tipo de tiempo de ejecución no lo admitiría.Trataré de aclarar la respuesta de Anthony Pegram.
El tipo genérico es covariante en algún argumento de tipo cuando devuelve valores de dicho tipo (por
Func<out TResult>
ejemploTResult
,IEnumerable<out T>
devuelve instancias de , devuelve instancias deT
). Es decir, si algo devuelve instancias deTDerived
, también puede trabajar con instancias como si fueran deTBase
.El tipo genérico es contravariante en algún argumento de tipo cuando acepta valores de dicho tipo (por ejemplo,
Action<in TArgument>
acepta instancias deTArgument
). Es decir, si algo necesita instancias deTBase
, también puede pasar instancias deTDerived
.Parece bastante lógico que los tipos genéricos que aceptan y devuelven instancias de algún tipo (a menos que se defina dos veces en la firma de tipo genérico, por ejemplo
CoolList<TIn, TOut>
) no son covariantes ni contravariantes en el argumento de tipo correspondiente. Por ejemplo,List
se define en .NET 4 comoList<T>
, noList<in T>
oList<out T>
.Algunas razones de compatibilidad podrían haber causado que Microsoft ignore ese argumento y haga que las matrices sean covariantes en su argumento de tipo de valores. Tal vez realizaron un análisis y descubrieron que la mayoría de las personas solo usan matrices como si fueran de solo lectura (es decir, solo usan inicializadores de matriz para escribir algunos datos en una matriz) y, como tal, las ventajas superan las desventajas causadas por el posible tiempo de ejecución errores cuando alguien intentará hacer uso de la covarianza al escribir en la matriz. Por lo tanto, está permitido pero no es alentado.
En cuanto a su pregunta original,
list.ToArray()
crea una nuevaLinkLabel[]
con valores copiados de la lista original y, para deshacerse de la advertencia (razonable), deberá pasarControl[]
aAddRange
.list.ToArray<Control>()
hará el trabajo:ToArray<TSource>
aceptaIEnumerable<TSource>
como argumento y devuelveTSource[]
;List<LinkLabel>
implementa solo lecturaIEnumerable<out LinkLabel>
, que, gracias a laIEnumerable
covarianza, podría pasarse al método que aceptaIEnumerable<Control>
como argumento.fuente
La "solución" más directa
flPanel.Controls.AddRange(_list.AsEnumerable());
Ahora, dado que está cambiando covariantemente
List<LinkLabel>
a,IEnumerable<Control>
no hay más preocupaciones, ya que no es posible "agregar" un elemento a un elemento enumerable.fuente
La advertencia se debe al hecho de que, en teoría, podría agregar
Control
otro que no seaLinkLabel
aLinkLabel[]
través de laControl[]
referencia. Esto provocaría una excepción en tiempo de ejecución.La conversión está sucediendo aquí porque
AddRange
toma aControl[]
.En términos más generales, la conversión de un contenedor de un tipo derivado a un contenedor de un tipo base solo es seguro si no puede modificar el contenedor posteriormente de la manera que se acaba de describir. Las matrices no satisfacen ese requisito.
fuente
La causa raíz del problema se describe correctamente en otras respuestas, pero para resolver la advertencia, siempre puede escribir:
fuente
Con VS 2008, no recibo esta advertencia. Esto debe ser nuevo en .NET 4.0.
Aclaración: según Sam Mackrill, es Resharper quien muestra una advertencia.
El compilador de C # no sabe que
AddRange
no modificará la matriz que se le pasó. Dado queAddRange
tiene un parámetro de tipoControl[]
, en teoría podría intentar asignar unTextBox
a la matriz, lo que sería perfectamente correcto para una verdadera matriz deControl
, pero la matriz es en realidad una matriz deLinkLabels
y no aceptará dicha asignación.Hacer matrices covariantes en C # fue una mala decisión de Microsoft. Si bien puede parecer una buena idea poder asignar una matriz de un tipo derivado a una matriz de un tipo base en primer lugar, ¡esto puede conducir a errores de tiempo de ejecución!
fuente
¿Qué tal esto?
fuente
_list.ToArray<Control>()
.