开发者

C# - Can a List<MyClass> be seamlessly cast to a List<Interface> or similar?

I have a DataSource in my control which is always a List<T> where T has to inherit from IEntity.

public class MyClass<T> where T : IEntity
{
    public List<T> DataSource
    {
        get;
        set;
    }
}

Now, obviously you can't cast a List<T> to a List<IEntity> doing the following:

List<IEntity&开发者_如何学运维gt; wontWork = (List<IEntity>)this.DataSource;

How can I get the DataSource as a List of IEntity, whilst still being able to add and remove items from the DataSource? I.e. I could do the following, but removing from the List it returns would not remove from the DataSource:

public List<TOut> GetDataSourceCopyAsUnderlyingType<TOut>()
{

    if (this.DataSource == null)
    {
        return new List<TOut>();
    }
    else
    {

        // Get the list and the enumerator
        IList list = (IList)this.DataSource;
        IEnumerator enumerator = list.GetEnumerator();

        // Build the target list
        List<TOut> targetList = new List<TOut>();

        int i = 0;
        while (enumerator.MoveNext())
        {
            TOut entity = (TOut)list[i];
            targetList.Add(entity);
            i++;
        }

        return targetList;

        }

    }

Basically, I need some way of doing the following:

List<IEntity> interfaceList = this.GetDataSourceAsAnotherType<IEntity>();
int dataSourceCount = this.DataSource.Count;   // Equals 5
int interfaceCount = interfaceList.Count;      // Equals 5

interfaceList.RemoveAt(0);
int dataSourceCount = this.DataSource.Count;   // Equals 4
int interfaceCount = interfaceList.Count;      // Equals 4

And just to add, I don't mind if it means I've got to use a different type instead of a List.

EDIT: Sorry, forgot to say I'm using .Net2.0 and cannot move to .Net 3.5.


It would be a monumentally bad idea if this were allowed, which is why it isn't. I can add any old IEntity to a List<IEntity> which will blow up if that IEntity can't be cast to T. Whilst all Ts are IEntities, not all IEntities are Ts.

This works with arrays because arrays have a deliberate subtyping hole (as they do in Java). Collections do not have a subtyping hole.


Create a wrapper class that seamlessly converts. Untested sample:

public class CastList<TTarget, TOriginal> 
  : IList<TTarget> where TOriginal : TTarget
{
  List<TOriginal> _orig;
  public CastList(List<TOriginal> orig) { _orig = orig; }

  public Add(TTarget item) { _orig.Add(item); }

  public TTarget this[int i] 
  {
    get { return (TTarget)_orig[i]; }
    set { _orig[i] = value; }
  }

  public IEnumerator<TTarget> GetEnumerator() 
  {
     foreach(TOriginal item in _orig)
       yield return (TTarget)item;
  }

  // etc...
}

Manipulations of the original list will also be reflected in the wrapper. To use this, just construct it with your DataSource.


What DrPizza said, but with more code:

public class ListFacade<TIn, TOut> : IList<TOut> where TIn : TOut
{
    private readonly IList<TIn> innerList;

    public ListFacade(IList<TIn> innerList)
    {
        this.innerList = innerList;
    }

    public int Count
    {
        get { return this.innerList.Count; }
    }

    public bool IsReadOnly
    {
        get { return this.innerList.IsReadOnly; }
    }

    public TOut this[int index]
    {
        get { return this.innerList[index]; }
        set { this.innerList[index] = (TIn)value; }
    }

    public void Add(TOut item)
    {
        this.innerList.Add((TIn)item);
    }

    public void Clear()
    {
        this.innerList.Clear();
    }

    public bool Contains(TOut item)
    {
        return (item is TIn) && this.innerList.Contains((TIn)item);
    }

    public void CopyTo(TOut[] array, int arrayIndex)
    {
        var inArray = new TIn[this.innerList.Count];
        this.innerList.CopyTo(inArray, arrayIndex);
        Array.Copy(inArray, array, inArray.Length);
    }

    public IEnumerator<TOut> GetEnumerator()
    {
        foreach (var item in this.innerList)
        {
            yield return item;
        }
    }

    System.Collections.IEnumerator
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public int IndexOf(TOut item)
    {
        return (item is TIn) ? this.innerList.IndexOf((TIn)item) : -1;
    }

    public void Insert(int index, TOut item)
    {
        this.innerList.Insert(index, (TIn)item);
    }

    public bool Remove(TOut item)
    {
        return (item is TIn) && this.innerList.Remove((TIn)item);
    }

    public void RemoveAt(int index)
    {
        this.innerList.RemoveAt(index);
    }

Add, Insert and the indexer set will blow up if the argument is not of type TIn.


ok this might be completely beside the point but, how about using a little bit of Linq?

var interfaceList = objectList.ConvertAll<Interface>(o => (Interface)o);

this way you can cast the objectList easily. hope this helps to find the solution...


I'm in favor of linq too, but you can do it like:

var interfaceList = objectList.Cast<IEntity>();

Which is shorter and more expressive.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜