虽然可以从基类/接口继承,但为什么不能声明List<>呢 使用相同的类/接口?

interface A
{ }

class B : A
{ }

class C : B
{ }

class Test
{
    static void Main(string[] args)
    {
        A a = new C(); // OK
        List<A> listOfA = new List<C>(); // compiler Error
    }
}

有别的办法吗?


当前回答

我个人喜欢创建带有类扩展的类库

public static List<TTo> Cast<TFrom, TTo>(List<TFrom> fromlist)
  where TFrom : class 
  where TTo : class
{
  return fromlist.ConvertAll(x => x as TTo);
}

其他回答

引用埃里克的伟大解释

会发生什么呢?你想在长颈鹿的名单中包含一只老虎吗?你想撞车吗?或者你希望编译器通过在第一时间使赋值非法来保护你不崩溃? 我们选择后者。

但是,如果希望选择运行时崩溃而不是编译错误,该怎么办呢?你通常会使用Cast<>或ConvertAll<>,但你会有两个问题:它会创建一个列表的副本。如果您在新列表中添加或删除某些内容,这将不会反映在原始列表中。其次,由于它用现有对象创建了一个新列表,因此会损失很大的性能和内存。

我也遇到了同样的问题,因此我创建了一个包装器类,它可以强制转换一个泛型列表,而无需创建一个全新的列表。

在原来的问题中,你可以用:

class Test
{
    static void Main(string[] args)
    {
        A a = new C(); // OK
        IList<A> listOfA = new List<C>().CastList<C,A>(); // now ok!
    }
}

这里是包装器类(+一个扩展方法CastList,便于使用)

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; } }
}

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

这是对BigJim的精彩回答的延伸。

在我的例子中,我有一个带有子字典的NodeBase类,我需要一种方法来从子字典中进行O(1)查询。我试图在Children的getter中返回一个私有字典字段,所以显然我想避免昂贵的复制/迭代。因此,我使用Bigjim的代码将Dictionary<whatever特定类型>转换为generic Dictionary<NodeBase>:

// Abstract parent class
public abstract class NodeBase
{
    public abstract IDictionary<string, NodeBase> Children { get; }
    ...
}

// Implementing child class
public class RealNode : NodeBase
{
    private Dictionary<string, RealNode> containedNodes;

    public override IDictionary<string, NodeBase> Children
    {
        // Using a modification of Bigjim's code to cast the Dictionary:
        return new IDictionary<string, NodeBase>().CastDictionary<string, RealNode, NodeBase>();
    }
    ...
}

这很有效。然而,我最终遇到了不相关的限制,并最终在基类中创建了一个抽象的FindChild()方法来代替它进行查找。事实证明,这从一开始就消除了对强制转换字典的需求。(出于我的目的,我可以用一个简单的IEnumerable替换它。)

所以你可能会问的问题(特别是当性能问题禁止你使用.Cast<>或.ConvertAll<>)是:

“我真的需要强制转换整个集合吗?或者我可以使用抽象方法来保存执行任务所需的特殊知识,从而避免直接访问集合吗?”

有时候最简单的解决方案就是最好的。

我个人喜欢创建带有类扩展的类库

public static List<TTo> Cast<TFrom, TTo>(List<TFrom> fromlist)
  where TFrom : class 
  where TTo : class
{
  return fromlist.ConvertAll(x => x as TTo);
}

至于为什么它不起作用,理解协方差和逆变可能会有所帮助。

只是为了说明为什么这不能工作,这里是对您提供的代码的更改:

void DoesThisWork()
{
     List<C> DerivedList = new List<C>();
     List<A> BaseList = DerivedList;
     BaseList.Add(new B());

     C FirstItem = DerivedList.First();
}

这可行吗?列表中的第一个项是类型“B”,但派生列表项的类型是C。

现在,假设我们真的只想创建一个泛型函数,它对实现了a的某个类型的列表进行操作,但我们不关心它是什么类型:

void ThisWorks<T>(List<T> GenericList) where T:A
{

}

void Test()
{
     ThisWorks(new List<B>());
     ThisWorks(new List<C>());
}

可能会迟到。

转换为数组也可以完成这项工作。

main()
{
   List<Camel> camels = new List<Camel>();
   Reproducton(camels.ToArray());
}


public void Reproducton(Animal[] animals)
{
    foreach(var animal in animals.ToList())
    {
       var baby = animal.Reproduce();
    }
}