Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upcasting and generic lists

Tags:

c#

.net

I have the following class hierarchy:

public abstract class BaseData
{
  //some properties
}

public class CoData : BaseData
{
  //some properties
}

I am working with a method that requires the return type to be List<BaseData>. In the method, I have access to List<CoData>

public List<BaseData> Save()
{
  List<CoData> listCoData = GetData();
  return listCoData;
}

If I understand correctly, I can upcast from a CoData to a BaseData. But, when I have a list, it errors out even if I explicitly try to typecast.

Error:

Error   118 Cannot implicitly convert type 'System.Collections.Generic.List<CoData>' to System.Collections.Generic.List<BaseData>'

EDIT:

mquander's Conversion approach seems to work for me in 3.0

Is downcasting done the same way as well? from

ie., Can I do this - List<CoData> listCoData = listBaseData.Cast<BaseData>().ToList();

like image 773
DotnetDude Avatar asked Apr 08 '09 14:04

DotnetDude


2 Answers

Yes; welcome to variance. Ultimately, it isn't a list of BaseData - for example, if you had another subclass, a List<BaseData> would (at compile time) let you .Add it... but the runtime type wouldn't let you. The compiler is stopping you making a mistake.

In some scenarios, generics can help here... I discuss this at the end of this blog entry. Note that .NET 4.0 variance doesn't apply to lists.

like image 140
Marc Gravell Avatar answered Sep 22 '22 15:09

Marc Gravell


This is called "covariance" with respect to the parameter of your collection, and C# doesn't support this kind of covariance right now. In C# 4.0, user-defined types and some built-in types like IEnumerable<T> will support this.

In the meantime, you can work around it by creating a new enumeration with explicitly cast members (you might want to think about changing the return types of your stuff here to IEnumerable<T> so you don't have to actually cook up new collections all the time:)

public List<BaseData> Save()
{
    List<CoData> listCoData = GetData();
    return listCoData.Cast<BaseData>().ToList();
}

Depending on how GetData works, you might also consider a structure like this:

public List<T> Save<T>() where T : BaseData
{
    return listCoData = GetData<T>();
}

EDIT: Actually, as Marc and others undoubtedly will point out, List<T> can't be covariant like this because you can add members in ways that would break type safety if it were. However, if you sent back IEnumerable<T>s instead, you could use covariance here.

EDIT 2: In response to your additional question:

Can I do this? List<CoData> listCoData = listBaseData.Cast<BaseData>().ToList();

No. listBaseData.Cast<BaseData>().ToList(); returns an object of type List<BaseData> which can't be cast directly to a List<CoData>. That's why we had to go to this effort in the first place.

like image 35
mqp Avatar answered Sep 21 '22 15:09

mqp