2010-09-27 11 views
12

posible duplicado:
Adding null to a List<bool?> cast as an IList throwing an exception.No se puede agregar a la lista de nula nullables

List<int?> listONullables = new List<int?>(); 
IList degenericed = listONullables; 

// This works fine 
listONullables.Add(null); 

// Run time exception: 
// "The value "" is not of type "System.Nullable`1[System.Int32]" 
// and cannot be used in this generic collection. Parameter name: value" 
degenericed.Add(null); 

// Also does not work. Same exception 
degenericed.Add((int?)null); 

// Also does not work 
// EDIT: I was mistaken, this does work 
degenericed.Add((int?)1); 

// Also does not work 
// EDIT: I was mistaken, this does work 
degenericed.Add(1); 

Ver los comentarios en el código de seguridad.

Entiendo las razones para esto (cuando descarta los genéricos, el tiempo de ejecución lo hace lo mejor que puede con información limitada). Me pregunto si hay una forma de evitar esto, incluso si es un truco.

El problema surgió cuando intenté que la versión genérica de una función utilizara la misma implementación privada que una versión no genérica, así que puedo evitarla si es necesario (tengo dos implementaciones muy similares), pero obviamente es mejor si Puedo resolver esto.

EDITAR: Las últimas dos entradas que he mencionado anteriormente NO fallan como dije originalmente. Pero los primeros dos sí. He agregado comentarios a tal efecto en el código anterior.

+7

Su código funcionó perfectamente para mí sin excepción cuando lo probé. –

+1

Puedo confirmar la excepción en el segundo ejemplo: '.Add ((int?) Null)', .NET 3.5 – Aren

+1

Con 2 positivos y 1 negativo, es hora de que todo el mundo empiece a mencionar versiones de compiladores, etc. –

Respuesta

5

Para más detalles sobre la discusión en los comentarios, parece que en List<T>.IList.Add en 4.0, se encuentra:

ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item); 
try 
{ 
    this.Add((T) item); 
} 
catch (InvalidCastException) 
{ 
    ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); 
} 

Y 2.0 tiene VerifyValueType que simplemente se comprueba el método IsCompatibleObject:

VerifyValueType(item); 

... 

private static bool IsCompatibleObject(object value) { 
    if((value is T) || (value == null && !typeof(T).IsValueType)) { 
     return true; 
    } 
    return false; 
} 

El este último está escrito de manera simplista. value no es T (porque null no es lo mismo que Nullable<int>.HasValue = false). Además, como notas de @LBushkin, typeof (T) .IsValueType devolverá true para Nullable<int> y por lo tanto, el lado derecho también se evalúa como falso.

+0

Creo que el problema aquí es que '! Typeof (T) .IsValueType' se evalúa como falso cuando' T' es 'Nullable ' y 'value is T 'también evalúa para falso. En consecuencia, la verificación falla y no puede agregar un nulo a través de esta implementación. La implementación de .NET 4.0 simplemente delega a la implementación genérica 'List .Add' que maneja correctamente este caso. – LBushkin

+0

@LBushkin, tienes razón, también era necesario abordarlo. Actualizará. –

+0

@Kirk: El uso de 'degenericed.Add (new Nullable ())' también falla, ya que es equivalente a 'degenericed.Add ((int?) Null)'. El resultado final no es diferente de pasar el 'simple' simple en el método' Add'. – LukeH

1

Esto funciona en .NET 4.0 con la introducción de covarianza y contravarianza.

Dado que usted no está en 4.0 (obviamente debido al error de ejecución) se puede evitar que al pasar por defecto (int) para obtener un valor nulo

ACTUALIZACIÓN: No me escucha por defecto (int) = 0 NO nulo. Estoy retrasada :(

Esto funciona para nulo:?

degenericed.Add(default(int)); 

La llamada complemento funciona correctamente para mí, sin embargo

degenericed.Add(1); 
+0

No creo que esto tenga nada que ver con los cambios de varianza en .NET 4: es solo un error en las versiones anteriores del marco. – LukeH

0

tratar de cambiar la línea:

IList degenericed = listONullables; 

por esto:

IList<int?> degenericed = listONullables; 
2

Este es un error de los 3,5 marco (y probablemente versiones anteriores también). El resto de esta respuesta se relaciona con .NET 3.5, aunque los comentarios sugieren que el error se ha corregido en la versión 4 del marco ...

Cuando se pasa un tipo de valor al método IList.Add que aparece dentro como object debido a la interfaz de IList siendo no genérico. La única excepción a esta regla son null tipos que aceptan nulos que se convierten (no en cajas) a simple null.

El método IList.Add en los List<T> clase comprueba que el tipo que está tratando de añadir es en realidad un T, pero la comprobación de compatibilidad no toma null tipos anulables en cuenta:

cuando se pasa null , el control de compatibilidad sabe que su lista es List<int?> y sabe que int? es un tipo de valor, pero - aquí está el error - arroja un error porque también "sabe" que los tipos de valores no pueden ser null, ergo el null que pasaste no puede ser int? .

+0

, de hecho, no arroja un error para '1'. –

+0

@Kirk: ¡Ups, corregido! – LukeH

Cuestiones relacionadas