Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type conversion for generic lists in C#

Tags:

c#

generics

I'm trying to figure out why the type conversion is not possible in the first case, but it is possible in the second one. Please, take a look at the code below:

var strList = new List<string>{"One", "Two", "Three"};
List<object> objList = (List<object>) strList; // <<<< why is not converted? - Case 1

IEnumerable<object> ienumList = strList; // <<<< why is converted?  - Case 2
like image 354
Pavel Nv Avatar asked Jan 23 '17 12:01

Pavel Nv


People also ask

How do you convert a generic list?

Select(x => new TestClass() { AAA = (string)x[0], BBB = (string)x[1], CCC = (string)x[2] }). ToList();

Can dynamic type be used for generic?

Not really. You need to use reflection, basically. Generics are really aimed at static typing rather than types only known at execution time.

How do you find a generic type?

Use the IsGenericType property to determine whether the type is generic, and use the IsGenericTypeDefinition property to determine whether the type is a generic type definition. Get an array that contains the generic type arguments, using the GetGenericArguments method.

What is generic type in C#?

Generic is a class which allows the user to define classes and methods with the placeholder. Generics were added to version 2.0 of the C# language. The basic idea behind using Generic is to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes, and interfaces.


2 Answers

While the comment referring to covariance and contravariance makes a good point about the underlying principle, I am going to answer in terms of the concrete effects in the presented case:

List<object> objList = (List<object>) strList;

objList is writeable. It will allow you to do this:

objList.Add(new Object());

However, objList is still the same instance as strList. You have just added an instance of System.Object to a List<string>!

The compiler cannot allow that to happen. Thus, a List<string> cannot be cast to a List<object>.

In the second case, on the other hand, you retrieve an enumerable:

IEnumerable<object> ienumList = strList;

There is no way to modify the resulting enumerable, as IEnumerable<T> does not provide any members that mutate the instance. Therefore, the type parameter T can be (and is) marked with the out keyword, which allows the presented typecast.

like image 161
O. R. Mapper Avatar answered Oct 06 '22 09:10

O. R. Mapper


Two major concepts play roles here: Type Conversions and Generic Interfaces Variance. Where the variance is the leading one.

Case 1: In List<T> class definition, we do not have variance on generic parameter. So we do not have any relations defined between List<object> and List<string>. They are invariant. Therefore both implicit and explicit type conversion are not possible.

Case 2: List<T> implements IEnumerable<out T> which is covariant generic type, so List<string> can be implicitly cast to IEnumerable<object>

DETAILS:

Why the variance is not allowed for List<T>, but allowed for IEnumerable<T>?

The point of generics is to provide compile-time type safety. Because List<T> is writable and if there wasn't a compile time check, we could write the following and have a run-time error:

List<string> stringList = new List<string>();
stringList.Add("some string"); // we are safe
List<object> objectList = stringList;
objectList.Add((new Object()); // Aargh! 
// we are trying to put an object to a list of strings!

So no unsafe variance for List<T>.

But IEnumerable<out T> is read-only. It does not provide a way to modify the referenced instance.

IEnumerable<object> objectList = new List<string>();
// we can't add a string to the objectList, 
// as `IEnumerable<out T>` is a read-only interface.

So a safe variance is possible.

like image 22
kalitsov Avatar answered Oct 06 '22 07:10

kalitsov