Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't add null to list of nullables [duplicate]

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.

like image 672
Jay Lemmon Avatar asked Sep 27 '10 22:09

Jay Lemmon


People also ask

Can null be added to list C#?

In C# programs, a List reference can be null. This is not the same as it being empty and having zero elements.

Does list allow null values in Java?

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.

Can a list be null?

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.


2 Answers

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.

like image 110
Kirk Woll Avatar answered Oct 20 '22 01:10

Kirk Woll


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?.

like image 20
LukeH Avatar answered Oct 20 '22 00:10

LukeH