Assignability of Generic types? [duplicate] - c#

It seems that a List object cannot be stored in a List variable in C#, and can't even be explicitly cast that way.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
results in Cannot implicitly convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
And then...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
results in Cannot convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
Of course, you can do it by pulling everything out of the string list and putting it back in one at a time, but it is a rather convoluted solution.

Think of it this way, if you were to do such a cast, and then add an object of type Foo to the list, the list of strings is no longer consistent. If you were to iterate the first reference, you would get a class cast exception because once you hit the Foo instance, the Foo could not be converted to string!
As a side note, I think it would be more significant whether or not you can do the reverse cast:
List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;
I haven't used C# in a while, so I don't know if that is legal, but that sort of cast is actually (potentially) useful. In this case, you are going from a more general class (object) to a more specific class (string) that extends from the general one. In this way, if you add to the list of strings, you are not violating the list of objects.
Does anybody know or can test if such a cast is legal in C#?

If you're using .NET 3.5 have a look at the Enumerable.Cast method. It's an extension method so you can call it directly on the List.
List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();
It's not exactly what you asked for but should do the trick.
Edit: As noted by Zooba, you can then call ol.ToList() to get a List

You cannot cast between generic types with different type parameters. Specialized generic types don't form part of the same inheritance tree and so are unrelated types.
To do this pre-NET 3.5:
List<string> sl = new List<string>();
// Add strings to sl
List<object> ol = new List<object>();
foreach(string s in sl)
{
ol.Add((object)s); // The cast is performed implicitly even if omitted
}
Using Linq:
var sl = new List<string>();
// Add strings to sl
var ol = new List<object>(sl.Cast<object>());
// OR
var ol = sl.Cast<object>().ToList();
// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

The reason is that a generic class like List<> is, for most purposes, treated externally as a normal class. e.g. when you say List<string>() the compiler says ListString() (which contains strings). [Technical folk: this is an extremely plain-English-ified version of what's going on]
Consequently, obviously the compiler can't be smart enough to convert a ListString to a ListObject by casting the items of its internal collection.
That's why there's extension methods for IEnumerable like Convert() that allow you to easily supply conversion for the items stored inside a collection, which could be as simple as casting from one to another.

This has a lot to do with covariance, e.g., generic types are considered as parameters, and if the parameters do not resolve properly to a more specific type then the operation fails. The implication of such is that you really cannot cast to a more general type like object. And as stated by Rex, the List object won't convert each object for you.
You might want to try the ff code instead:
List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);
or:
List<object> ol = new List<object>();
ol.AddRange(sl);
ol will (theoretically) copy all the contents of sl without problems.

Yes, you can, from .NET 3.5:
List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();

Mike - I believe contravariance isn't allowed in C# either
See Generic type parameter variance in the CLR for some more info.

I think that this (contravariance) will actually be supported in C# 4.0.
http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

That's actually so that you don't try to put any odd "object" in your "ol" list variant (as List<object> would seem to allow) - because your code would crash then (because the list really is List<string> and will only accept String type objects). That's why you can't cast your variable to a more general specification.
On Java it's the other way around, you don't have generics, and instead everything is List of object at runtime, and you really can stuff any strange object in your supposedly-strictly typed List. Search for "Reified generics" to see a wider discussion of java's problem...

Such covariance on generics is not supported, but you can actually do this with arrays:
object[] a = new string[] {"spam", "eggs"};
C# performs runtime checks to prevent you from putting, say, an int into a.

Here is another pre-.NET 3.5 solution for any IList whose contents can be cast implicitly.
public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
List<B> newList = new List<B>();
foreach (D item in list)
{
newList.Add(item);
}
return newList;
}
(Based on Zooba's example)

I have a:
private List<Leerling> Leerlingen = new List<Leerling>();
And I was going to fill it with data collected in an List<object>
What finally worked for me was this one:
Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();
.Cast it to the type you want to get an IEnumerable from that type, then typecast the IEnemuerable to the List<> you want.

Mm, thanks to previous comments I found two ways to find it out. The first one is getting the string list of elements and then casting it to IEnumerable object list:
IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();
And the second one is avoiding the IEnumerable object type, just casting the string to object type and then using the function "toList()" in the same sentence:
List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();
I like more the second way. I hope this helps.

List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);

Related

Generics conversion by OOP [duplicate]

It seems that a List object cannot be stored in a List variable in C#, and can't even be explicitly cast that way.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
results in Cannot implicitly convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
And then...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
results in Cannot convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
Of course, you can do it by pulling everything out of the string list and putting it back in one at a time, but it is a rather convoluted solution.
Think of it this way, if you were to do such a cast, and then add an object of type Foo to the list, the list of strings is no longer consistent. If you were to iterate the first reference, you would get a class cast exception because once you hit the Foo instance, the Foo could not be converted to string!
As a side note, I think it would be more significant whether or not you can do the reverse cast:
List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;
I haven't used C# in a while, so I don't know if that is legal, but that sort of cast is actually (potentially) useful. In this case, you are going from a more general class (object) to a more specific class (string) that extends from the general one. In this way, if you add to the list of strings, you are not violating the list of objects.
Does anybody know or can test if such a cast is legal in C#?
If you're using .NET 3.5 have a look at the Enumerable.Cast method. It's an extension method so you can call it directly on the List.
List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();
It's not exactly what you asked for but should do the trick.
Edit: As noted by Zooba, you can then call ol.ToList() to get a List
You cannot cast between generic types with different type parameters. Specialized generic types don't form part of the same inheritance tree and so are unrelated types.
To do this pre-NET 3.5:
List<string> sl = new List<string>();
// Add strings to sl
List<object> ol = new List<object>();
foreach(string s in sl)
{
ol.Add((object)s); // The cast is performed implicitly even if omitted
}
Using Linq:
var sl = new List<string>();
// Add strings to sl
var ol = new List<object>(sl.Cast<object>());
// OR
var ol = sl.Cast<object>().ToList();
// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
The reason is that a generic class like List<> is, for most purposes, treated externally as a normal class. e.g. when you say List<string>() the compiler says ListString() (which contains strings). [Technical folk: this is an extremely plain-English-ified version of what's going on]
Consequently, obviously the compiler can't be smart enough to convert a ListString to a ListObject by casting the items of its internal collection.
That's why there's extension methods for IEnumerable like Convert() that allow you to easily supply conversion for the items stored inside a collection, which could be as simple as casting from one to another.
This has a lot to do with covariance, e.g., generic types are considered as parameters, and if the parameters do not resolve properly to a more specific type then the operation fails. The implication of such is that you really cannot cast to a more general type like object. And as stated by Rex, the List object won't convert each object for you.
You might want to try the ff code instead:
List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);
or:
List<object> ol = new List<object>();
ol.AddRange(sl);
ol will (theoretically) copy all the contents of sl without problems.
Yes, you can, from .NET 3.5:
List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Mike - I believe contravariance isn't allowed in C# either
See Generic type parameter variance in the CLR for some more info.
I think that this (contravariance) will actually be supported in C# 4.0.
http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
That's actually so that you don't try to put any odd "object" in your "ol" list variant (as List<object> would seem to allow) - because your code would crash then (because the list really is List<string> and will only accept String type objects). That's why you can't cast your variable to a more general specification.
On Java it's the other way around, you don't have generics, and instead everything is List of object at runtime, and you really can stuff any strange object in your supposedly-strictly typed List. Search for "Reified generics" to see a wider discussion of java's problem...
Such covariance on generics is not supported, but you can actually do this with arrays:
object[] a = new string[] {"spam", "eggs"};
C# performs runtime checks to prevent you from putting, say, an int into a.
Here is another pre-.NET 3.5 solution for any IList whose contents can be cast implicitly.
public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
List<B> newList = new List<B>();
foreach (D item in list)
{
newList.Add(item);
}
return newList;
}
(Based on Zooba's example)
I have a:
private List<Leerling> Leerlingen = new List<Leerling>();
And I was going to fill it with data collected in an List<object>
What finally worked for me was this one:
Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();
.Cast it to the type you want to get an IEnumerable from that type, then typecast the IEnemuerable to the List<> you want.
Mm, thanks to previous comments I found two ways to find it out. The first one is getting the string list of elements and then casting it to IEnumerable object list:
IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();
And the second one is avoiding the IEnumerable object type, just casting the string to object type and then using the function "toList()" in the same sentence:
List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();
I like more the second way. I hope this helps.
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);

c# access elements of list passed as object type

I want to access members of lists passed in as "object" (the function is handling other data types such as arrays as well). Unfortunately, while this works:
List<object> objectlist = new List<object> { "1", "2" };
object res = ((List<object>)((object)objectlist))[0];
this does not:
List<string> strlist = new List<string> { "1", "2" };
res = (string)((List<object>)((object)strlist))[0];
though it DOES work with arrays.
It does not appear to be possible to convert regular lists to List.
Is using reflection (but with GetMethods to avoid repeated string searches):
MethodInfo info = ((object)list).GetType().GetMethod("get_Item");
object s1 = (object)info.Invoke(((object)list), new object[] { 0 });
the only way to do this?
No, a List<string> isn't a List<object>... although it is an IEnumerable<object> due to generic covariance. However, a List<int> isn't even an IEnumerable<object> as covariance doesn't apply to value type type arguments. Fortunately, it is an IEnumerable.
So to get the first element of an arbitrary object which you know implements IEnumerable, I'd just use:
object first = ((IEnumerable) source).Cast<object>().First();
(Due to IEnumerator not implementing IDisposable, more "manual" ways of doing it end up being a bit fiddly.)
List<T> implements non-generic IList. If you only want to retrieve items from the list, you can cast to it. One-dimensional arrays implement it too, so the cast will work for both arrays and lists
res = (string)((IList)((object)strlist))[0];

Cast List<Y> to List<X> fast, without new List creation

I need to cast List with references to base type to list with references to derived type. I can't use LINQ ToList, because it creates new list and this is slow for me. Is there in C# some quick cast, maybe like in C++. Thanks.
You can't do that. And there is a reason for it. Consider the following.
List<Animal> animals = new List<Cat>();
animals.Add(new Dog());
You lose the type safety of the List<T> here, this is why List is not covariant on T. What you can do is cast it to IEnumerable<X>, because IEnumerable only supports getting an element and this is why IEnumerable is covariant on its type parameter.
You don't need to create a new list if you want to just iterate the elements, without changing anything about the list itselft.
Do you actually need a new list? Can you use LINQ OfType<T>() or Cast<T>()?
var baseList = new List<BaseType>();
var derivedItems = baseList.OfType<DerivedType>();
Have you tried:
List<Y> listY = new List<Y>()
var listX = ListY.Select(y => new X(y));
This will create an IEnumerable<X> that you can enumerate
LINQ Cast() is going to be you friend here. However, be wary: if you have anything not castable, this code is going to crash.
List<object> newList= new List<Object>();
newList.Add("SomeString");
foreach( string s in newList.Cast<string>())
{
//Do something
}
You can't cast one type to another without creating something new.
Hence, the only option you have is to create a new list, or have something that can iterate over the existing list and acts like a type of another. You could implement your own enumerator, or simply create a method returning a IEnumerable<TypeY> and yield return inside (which kind of create an enumerator).
public IEnumerable<TypeY> Convert(IEnumerable<TypeX> listX)
{
foreach (TypeX x in listX)
{
yield return ConvertXToY(x);
}
}

why can't I assign a list of an object to an IList of the interface it implements [duplicate]

It seems that a List object cannot be stored in a List variable in C#, and can't even be explicitly cast that way.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
results in Cannot implicitly convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
And then...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
results in Cannot convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
Of course, you can do it by pulling everything out of the string list and putting it back in one at a time, but it is a rather convoluted solution.
Think of it this way, if you were to do such a cast, and then add an object of type Foo to the list, the list of strings is no longer consistent. If you were to iterate the first reference, you would get a class cast exception because once you hit the Foo instance, the Foo could not be converted to string!
As a side note, I think it would be more significant whether or not you can do the reverse cast:
List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;
I haven't used C# in a while, so I don't know if that is legal, but that sort of cast is actually (potentially) useful. In this case, you are going from a more general class (object) to a more specific class (string) that extends from the general one. In this way, if you add to the list of strings, you are not violating the list of objects.
Does anybody know or can test if such a cast is legal in C#?
If you're using .NET 3.5 have a look at the Enumerable.Cast method. It's an extension method so you can call it directly on the List.
List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();
It's not exactly what you asked for but should do the trick.
Edit: As noted by Zooba, you can then call ol.ToList() to get a List
You cannot cast between generic types with different type parameters. Specialized generic types don't form part of the same inheritance tree and so are unrelated types.
To do this pre-NET 3.5:
List<string> sl = new List<string>();
// Add strings to sl
List<object> ol = new List<object>();
foreach(string s in sl)
{
ol.Add((object)s); // The cast is performed implicitly even if omitted
}
Using Linq:
var sl = new List<string>();
// Add strings to sl
var ol = new List<object>(sl.Cast<object>());
// OR
var ol = sl.Cast<object>().ToList();
// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
The reason is that a generic class like List<> is, for most purposes, treated externally as a normal class. e.g. when you say List<string>() the compiler says ListString() (which contains strings). [Technical folk: this is an extremely plain-English-ified version of what's going on]
Consequently, obviously the compiler can't be smart enough to convert a ListString to a ListObject by casting the items of its internal collection.
That's why there's extension methods for IEnumerable like Convert() that allow you to easily supply conversion for the items stored inside a collection, which could be as simple as casting from one to another.
This has a lot to do with covariance, e.g., generic types are considered as parameters, and if the parameters do not resolve properly to a more specific type then the operation fails. The implication of such is that you really cannot cast to a more general type like object. And as stated by Rex, the List object won't convert each object for you.
You might want to try the ff code instead:
List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);
or:
List<object> ol = new List<object>();
ol.AddRange(sl);
ol will (theoretically) copy all the contents of sl without problems.
Yes, you can, from .NET 3.5:
List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Mike - I believe contravariance isn't allowed in C# either
See Generic type parameter variance in the CLR for some more info.
I think that this (contravariance) will actually be supported in C# 4.0.
http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
That's actually so that you don't try to put any odd "object" in your "ol" list variant (as List<object> would seem to allow) - because your code would crash then (because the list really is List<string> and will only accept String type objects). That's why you can't cast your variable to a more general specification.
On Java it's the other way around, you don't have generics, and instead everything is List of object at runtime, and you really can stuff any strange object in your supposedly-strictly typed List. Search for "Reified generics" to see a wider discussion of java's problem...
Such covariance on generics is not supported, but you can actually do this with arrays:
object[] a = new string[] {"spam", "eggs"};
C# performs runtime checks to prevent you from putting, say, an int into a.
Here is another pre-.NET 3.5 solution for any IList whose contents can be cast implicitly.
public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
List<B> newList = new List<B>();
foreach (D item in list)
{
newList.Add(item);
}
return newList;
}
(Based on Zooba's example)
I have a:
private List<Leerling> Leerlingen = new List<Leerling>();
And I was going to fill it with data collected in an List<object>
What finally worked for me was this one:
Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();
.Cast it to the type you want to get an IEnumerable from that type, then typecast the IEnemuerable to the List<> you want.
Mm, thanks to previous comments I found two ways to find it out. The first one is getting the string list of elements and then casting it to IEnumerable object list:
IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();
And the second one is avoiding the IEnumerable object type, just casting the string to object type and then using the function "toList()" in the same sentence:
List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();
I like more the second way. I hope this helps.
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);

In C#, why can't a List<string> object be stored in a List<object> variable

It seems that a List object cannot be stored in a List variable in C#, and can't even be explicitly cast that way.
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
results in Cannot implicitly convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
And then...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
results in Cannot convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>
Of course, you can do it by pulling everything out of the string list and putting it back in one at a time, but it is a rather convoluted solution.
Think of it this way, if you were to do such a cast, and then add an object of type Foo to the list, the list of strings is no longer consistent. If you were to iterate the first reference, you would get a class cast exception because once you hit the Foo instance, the Foo could not be converted to string!
As a side note, I think it would be more significant whether or not you can do the reverse cast:
List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;
I haven't used C# in a while, so I don't know if that is legal, but that sort of cast is actually (potentially) useful. In this case, you are going from a more general class (object) to a more specific class (string) that extends from the general one. In this way, if you add to the list of strings, you are not violating the list of objects.
Does anybody know or can test if such a cast is legal in C#?
If you're using .NET 3.5 have a look at the Enumerable.Cast method. It's an extension method so you can call it directly on the List.
List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();
It's not exactly what you asked for but should do the trick.
Edit: As noted by Zooba, you can then call ol.ToList() to get a List
You cannot cast between generic types with different type parameters. Specialized generic types don't form part of the same inheritance tree and so are unrelated types.
To do this pre-NET 3.5:
List<string> sl = new List<string>();
// Add strings to sl
List<object> ol = new List<object>();
foreach(string s in sl)
{
ol.Add((object)s); // The cast is performed implicitly even if omitted
}
Using Linq:
var sl = new List<string>();
// Add strings to sl
var ol = new List<object>(sl.Cast<object>());
// OR
var ol = sl.Cast<object>().ToList();
// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
The reason is that a generic class like List<> is, for most purposes, treated externally as a normal class. e.g. when you say List<string>() the compiler says ListString() (which contains strings). [Technical folk: this is an extremely plain-English-ified version of what's going on]
Consequently, obviously the compiler can't be smart enough to convert a ListString to a ListObject by casting the items of its internal collection.
That's why there's extension methods for IEnumerable like Convert() that allow you to easily supply conversion for the items stored inside a collection, which could be as simple as casting from one to another.
This has a lot to do with covariance, e.g., generic types are considered as parameters, and if the parameters do not resolve properly to a more specific type then the operation fails. The implication of such is that you really cannot cast to a more general type like object. And as stated by Rex, the List object won't convert each object for you.
You might want to try the ff code instead:
List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);
or:
List<object> ol = new List<object>();
ol.AddRange(sl);
ol will (theoretically) copy all the contents of sl without problems.
Yes, you can, from .NET 3.5:
List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Mike - I believe contravariance isn't allowed in C# either
See Generic type parameter variance in the CLR for some more info.
I think that this (contravariance) will actually be supported in C# 4.0.
http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
That's actually so that you don't try to put any odd "object" in your "ol" list variant (as List<object> would seem to allow) - because your code would crash then (because the list really is List<string> and will only accept String type objects). That's why you can't cast your variable to a more general specification.
On Java it's the other way around, you don't have generics, and instead everything is List of object at runtime, and you really can stuff any strange object in your supposedly-strictly typed List. Search for "Reified generics" to see a wider discussion of java's problem...
Such covariance on generics is not supported, but you can actually do this with arrays:
object[] a = new string[] {"spam", "eggs"};
C# performs runtime checks to prevent you from putting, say, an int into a.
Here is another pre-.NET 3.5 solution for any IList whose contents can be cast implicitly.
public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
List<B> newList = new List<B>();
foreach (D item in list)
{
newList.Add(item);
}
return newList;
}
(Based on Zooba's example)
I have a:
private List<Leerling> Leerlingen = new List<Leerling>();
And I was going to fill it with data collected in an List<object>
What finally worked for me was this one:
Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();
.Cast it to the type you want to get an IEnumerable from that type, then typecast the IEnemuerable to the List<> you want.
Mm, thanks to previous comments I found two ways to find it out. The first one is getting the string list of elements and then casting it to IEnumerable object list:
IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();
And the second one is avoiding the IEnumerable object type, just casting the string to object type and then using the function "toList()" in the same sentence:
List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();
I like more the second way. I hope this helps.
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);

Categories