C# Casting a List<ObjBase> as List<Obj>

64,650

Solution 1

Please look at the following questions:

.NET Casting Generic List

Why does this generic cast fail?

Solution 2

You can use Cast and ToList extension methods from System.Linq to have this in one line.

Instead of

internal List<Obj> returnStuff()
{
   return getSomeStuff() as List<Obj>;
}

do this:

internal List<Obj> returnStuff()
{
   return getSomeStuff().Cast<Obj>().ToList();
}

Solution 3

I can only describe the "problem" from a Java view, but from what little I know this aspect is the same in both C# and Java:

A List<ObjBase> is not a List<Obj>, because it could contain an ObjBase object which is not a Obj object.

The other way around a List<Obj> can not be cast to a List<ObjBase> because the former guarantees to accept an Add() call with a ObjBase argument, which the latter will not accept!

So to summarize: even though a Obj is-a ObjBase a List<Obj> is not a List<ObjBase>.

Solution 4

Covariance my friend. Look at http://blog.t-l-k.com/dot-net/2009/c-sharp-4-covariance-and-contravariance

Solution 5

list.ConvertAll looks tempting but has 1 big disadvantage: it will create a whole new list. This will impact performance and memory usage especially for big lists.

With a bit more effort you can create a wrapper list class that keeps the original list as an internal reference, and convert the items only when they are used.

Usage:

var x = new List<ObjBase>();
var y = x.CastList<ObjBase, Obj>(); // y is now an IList<Obj>

Code to add to your library:

public static class Extensions
{
    public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list)
    {
        return new CastedList<TTo, TFrom>(list);
    }
}

public class CastedList<TTo, TFrom> : IList<TTo>
{
    public IList<TFrom> BaseList;

    public CastedList(IList<TFrom> baseList)
    {
        BaseList = baseList;
    }

    // IEnumerable
    IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); }

    // IEnumerable<>
    public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); }

    // ICollection
    public int Count { get { return BaseList.Count; } }
    public bool IsReadOnly { get { return BaseList.IsReadOnly; } }
    public void Add(TTo item) { BaseList.Add((TFrom)(object)item); }
    public void Clear() { BaseList.Clear(); }
    public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); }
    public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); }
    public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); }

    // IList
    public TTo this[int index]
    {
        get { return (TTo)(object)BaseList[index]; }
        set { BaseList[index] = (TFrom)(object)value; }
    }

    public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); }
    public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); }
    public void RemoveAt(int index) { BaseList.RemoveAt(index); }
}

public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo>
{
    public IEnumerator<TFrom> BaseEnumerator;

    public CastedEnumerator(IEnumerator<TFrom> baseEnumerator)
    {
        BaseEnumerator = baseEnumerator;
    }

    // IDisposable
    public void Dispose() { BaseEnumerator.Dispose(); }

    // IEnumerator
    object IEnumerator.Current { get { return BaseEnumerator.Current; } }
    public bool MoveNext() { return BaseEnumerator.MoveNext(); }
    public void Reset() { BaseEnumerator.Reset(); }

    // IEnumerator<>
    public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } }
}
Share:
64,650
Mohammad Heydari
Author by

Mohammad Heydari

I, you know, do stuff.

Updated on December 05, 2020

Comments

  • Mohammad Heydari
    Mohammad Heydari over 3 years

    Why can I not cast a List<ObjBase> as List<Obj>? Why does the following not work:

    internal class ObjBase
       {
       }
    
    internal class Obj : ObjBase
       {
       }   
    
    internal class ObjManager
    {
        internal List<Obj> returnStuff()
        {
           return getSomeStuff() as List<Obj>;
        }
    
        private List<ObjBase> getSomeStuff()
        {
           return new List<ObjBase>();
        }
    
    }
    

    Instead I have to do this:

    internal class ObjBase
       {
       }
    
    internal class Obj : ObjBase
       {
       }
    
    internal class ObjManager
    {
        internal List<Obj> returnStuff()
        {
           List<ObjBase> returnedList = getSomeStuff();
           List<Obj> listToReturn = new List<Obj>(returnedList.Count);
           foreach (ObjBase currentBaseObject in returnedList)
           {
              listToReturn.Add(currentBaseObject as Obj);
           }
           return listToReturn;
        }
    
        private List<ObjBase> getSomeStuff()
        {
           return new List<ObjBase>();
        }
    }
    

    I get the following error in Visual Studio 2008 (shortened for readability):

    Cannot convert type 'List' to 'List' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion

    Thanks.