Problem/Bug with the Generic List<T> class & nullable types
A college of mine recently ran into this...
Code SnippetList<Guid?> myGenericList = new List<Guid?>();
IList myList = myGenericList;
// This works.
myGenericList.Add(new Guid?());
// This doesn't work.
myList.Add(new Guid?());
The value "" is not of type "System.Nullable`1[System.Guid]" and cannot be used in this generic collection.
System.ArgumentException was unhandled
Message="The value \"\" is not of type \"System.Nullable`1[System.Guid]\" and cannot be used in this generic collection.\r\nParameter name: value"
Source="mscorlib"
ParamName="value"
StackTrace:
at System.ThrowHelper.ThrowWrongValueTypeArgumentException(Object value, Type targetType)
at System.Collections.Generic.List`1.VerifyValueType(Object value)
at System.Collections.Generic.List`1.System.Collections.IList.Add(Object item)
at ConsoleApplication3.Program.Main(String[] args) in C:\source\Blue\Hemisphere\WinUI\Suppliers\ConsoleApplication3\Program.cs:line 18
at System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Answers
The problem is because of Nullable<T> structure.
In int IList.Add(object item) method of the List<T> Generic class which is explicit Interface Implementation,
it uses IsCompatibleObject(object value) method (the one James Miles mentions) to verify value type.
You can browse with .net Reflector.But IList.Add method accept object param, so boxing and unboxing is needed.
The IsCompatibleObject(object value) method works well with the value types (except nullable<T>), e.g. int, decimal, and reference types, e.g. string.
When the value type(int) converts to object type, the value of object is 0 (default value of int). That is not null value.
But in your case, new Guid?() is null value and also ValueType (bcoz of nullable<T> struct). When it coverts to object type, the value of object is null value and also ValueType.
Therefore, IsCompatibleObject(object value) method return false and throw exception.In your case, you can use IList<Guid?> (for myList) or just use List<Guid> (for myGenericList). I normally use List<Guid> and use Guid.Empty to verify it's null.
All Replies
from List<T> decompilation
Code Snippetprivate static bool IsCompatibleObject(object value)
{
if (!(value is T) && ((value != null) || typeof(T).IsValueType))
{
return false;
}
return true;
}The problem is because of Nullable<T> structure.
In int IList.Add(object item) method of the List<T> Generic class which is explicit Interface Implementation,
it uses IsCompatibleObject(object value) method (the one James Miles mentions) to verify value type.
You can browse with .net Reflector.But IList.Add method accept object param, so boxing and unboxing is needed.
The IsCompatibleObject(object value) method works well with the value types (except nullable<T>), e.g. int, decimal, and reference types, e.g. string.
When the value type(int) converts to object type, the value of object is 0 (default value of int). That is not null value.
But in your case, new Guid?() is null value and also ValueType (bcoz of nullable<T> struct). When it coverts to object type, the value of object is null value and also ValueType.
Therefore, IsCompatibleObject(object value) method return false and throw exception.In your case, you can use IList<Guid?> (for myList) or just use List<Guid> (for myGenericList). I normally use List<Guid> and use Guid.Empty to verify it's null.
- Long story short, the others have touched on the bullet items, but you are pulling two different worlds together, Generics and collections. Though they share the same root namespace, there are differences to overcome and this is one.
The IList is from the top level collections namespace and works with non generic objects. To quote MSDN on IList
Represents a non-generic collection of objects that can be individually accessed by index.
Since the code is in a sense going from .Net 1 to .Net 2 in methodology (Generics were released in .Net 2) to mix and match will require extra coding on your part. I recommend that you keep generics with generics and collections with objects. >but you are pulling two different worlds together
Microsoft framework developers intended it to work like this otherwise List would not implement the following interfaces.
Code Snippet...}
>but you are pulling two different worlds together, Generics and collections.Actually, three different worlds are being pulled together. Collections, Generics & Nullable Types. Generics and Collections work fine together until Nullable Types are introduced.
Code SnippetList<Guid> myGenericList = new List<Guid>();
IList myList = myGenericList;
// This works.
myGenericList.Add(new Guid());
// This works.
myList.Add(new Guid());
- Soe has the correct answer. IList.Add(object) causes the Guid? to be boxed when can produce two outcomes, a null or a boxed Guid. That boxed value is now assignment compatible but not type compatible with the List<> element. This issue is highly specific to a nullable type, using IList with a normal reference type will not be an issue.
- Points well taken Miles. Thanks for the clarifications.


