Possible Duplicate:
Adding null to a List<bool?> cast as an IList throwing an exception.
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);
See the comments in the above code.
I sort of understand the reasons for this (when you cast away the generics the runtime does the best it can with limited information). I'm just wondering if there's a way around this, even if it's a bit of a hack.
The problem sprang up when I tried having the generic version of a function use the same private implementation as a non generic version, so I can work around it if necessary (have two very similar implementations), but obviously it's better if I can figure this out.
EDIT: The last two entries I have above do NOT fail like I originally said. But the first two do. I've added comments to that effect in the code above.
In C# programs, a List reference can be null. This is not the same as it being empty and having zero elements.
Solution. Yes, We can insert null values to a list easily using its add() method. In case of List implementation does not support null then it will throw NullPointerException.
A list is never null. The variable might be null, but a list itself is not null. Null is just null, and also has a specific intrinsic meaning. The following code demonstrates that there's no difference between a variable that's an uninitialized List versus an uninitialized Integer.
To elaborate on the discussion in the comments, it seems that in List<T>.IList.Add
in 4.0, there is:
ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item);
try
{
this.Add((T) item);
}
catch (InvalidCastException)
{
ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
}
And 2.0 has VerifyValueType which simply checks the IsCompatibleObject method:
VerifyValueType(item);
...
private static bool IsCompatibleObject(object value) {
if( (value is T) || ( value == null && !typeof(T).IsValueType) ) {
return true;
}
return false;
}
The latter is written in a simplistic fashion. value
is not T (because null is not the same as Nullable<int>.HasValue = false
). Also, as @LBushkin notes, typeof(T).IsValueType will return true for Nullable<int>
and so the right-hand-side also evaluates to false.
This is a bug in the 3.5 framework (and probably earlier versions too). The rest of this answer relates to .NET 3.5, although the comments suggest that the bug has been fixed in version 4 of the framework...
When you pass a value-type to the IList.Add
method it will be boxed as an object
due to the IList
interface being non-generic. The one exception to this rule are null
nullable types which are converted (not boxed) to plain null
.
The IList.Add
method on the List<T>
class checks that the type you're trying to add is actually a T
, but the compatibility check doesn't take null
nullable types into account:
When you pass null
, the compatibility check knows that your list is a List<int?>
and knows that int?
is a value-type, but -- here's the bug -- throws an error because it also "knows" that value-types cannot possibly be null
, ergo the null
that you passed cannot possibly be an int?
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With