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
Select(x => new TestClass() { AAA = (string)x[0], BBB = (string)x[1], CCC = (string)x[2] }). ToList();
Not really. You need to use reflection, basically. Generics are really aimed at static typing rather than types only known at execution time.
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.
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.
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.
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.
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