【问题标题】:Declare a superclass array声明一个超类数组
【发布时间】:2012-10-05 22:52:31
【问题描述】:

假设我有:

class A {
  //field, properties and common methods
}

class B : A {
  //specific ones
}

class C : A {
  //specific ones
}

class D : A {
  //specific ones
}

然后是这 3 个列表:

List<B> b_list = new List<B>();
List<C> c_list = new List<C>();
List<D> d_list = new List<D>();

我需要一个指向b_listc_listd_list 的列表。考虑到 B、C、D 类具有相同的超类 A,是否有可能?

类似:

List<A>[] lists = new[] {b_list, c_list, d_list};

谢谢

【问题讨论】:

  • 这些是列表,而不是数组。有重要的区别。

标签: c# .net arrays class superclass


【解决方案1】:

您想将List&lt;B&gt;List&lt;C&gt;List&lt;D&gt; 视为List&lt;A&gt; 的子类,但它们不是。它们唯一的共同超类型是object

在 .NET 4 及更高版本中,您可以将这些类型全部引用转换为 IEnumerable&lt;A&gt;,因为 IEnumerable&lt;out T&gt; 接口在 T 中是协变的。在 .NET 4.5 中,又添加了两个协变接口:IReadOnlyList&lt;out T&gt;IReadOnlyCollection&lt;out T&gt;,因此您也可以将它们视为 IReadOnlyList&lt;A&gt;IReadOnlyCollection&lt;A&gt;。这些界面之一可能适合您的需求。

类在 .NET 中不能是协变的,并且接口只能在类型参数中是协变的,前提是该类型参数仅用于“输出”位置。换句话说,IList&lt;T&gt;.Add(T item) 方法可以防止 IList&lt;&gt; 是协变的。

所以,假设 .NET 4.5,您可以做到这一点:

IReadOnlyList<A>[] lists = new[] {b_list, c_list, d_list};

在 .NET 4 中,您可以这样做,虽然用处不大,但可能足以满足您的需求:

IEnumerable<A>[] lists = new[] {b_list, c_list, d_list};

【讨论】:

  • 我不会说IEnumerable&lt;T&gt; 版本相当没那么有用。您仍然可以使用 LINQ(包括 ElementAt()),这样做也应该很快,因为 LINQ 实现会检查 IList&lt;T&gt; 并尽可能使用它。
  • @svick 但如果列表是List&lt;B&gt;,而您调用((IEnumerable&lt;A&gt;)theList).ElementAt(index),代码将检查对象是否实现IList&lt;A&gt;它没有实现,所以它会枚举列表来获取元素。
【解决方案2】:

不直接(covariance“限制”)

但你可以做到

List<A> superTypeList = 
   b_array.Cast<A>()
   .Concat(c_array.Cast<A>())
   .Concat(d_array.Cast<A>())
   .ToList();

【讨论】:

  • 该问题不要求包含三个列表中所有项目的列表,它要求一个列表(或数组)的列表。
【解决方案3】:

您必须列出 A 的列表并将 b、c、d 对象分配给它。您必须将子类对象强制转换为父类 A。这将产生限制,您将只能访问从基类继承的属性和方法等。

List<B> blist = new List<B>();
List<C> cList = new List<C>();
List<D> dList = new List<D>();

List<List<A>> arrays = new List<List<A>>();    
arrays.Add(b_array.ConvertAll(c=>(A)c));;

【讨论】:

  • 我不会丢失只在B/C/D类中声明的特定字段/方法/属性吗?
  • @FrankLioty 是的,您必须根据需要将 A 引用转换回 B、C 或 D。
  • 这个答案是polymorphism的一个很好的例子。
【解决方案4】:

更好的方法是使用称为Composite Pattern 的设计模式。

基本上,您创建一个基类(组件),其中包含您希望子节点(如果它们依次有子节点,则为 Compisites 节点,如果它们停止分支,则为叶节点)拥有的所有方法,然后仅实现您的方法希望每一个都在每个子类型中使用。

在你的情况下,你可以这样做:

abstract class A {   
    //All methods for all classes
    //ex.
    public abstract void b();
    public abstract void c();
    public abstract void d();
}  

class B : A {   
    public override void b()
    {
        //Do Something
    }
    public override void c()
    {
        throw new Exception("Not Implemented");
    }
    public override void d()
    {
        throw new Exception("Not Implemented");
    }
}  

class C : A {   
    public override void b()
    {
        throw new Exception("Not Implemented");
    }
    public override void c()
    {
        //Do Something
    }
    public override void d()
    {
        throw new Exception("Not Implemented");
    }
}  

class D : A {   
    public override void b()
    {
        throw new Exception("Not Implemented");
    }
    public override void c()
    {
        throw new Exception("Not Implemented");
    }
    public override void d()
    {
        //Do Something
    }
}

然后根据多态性的规律,如果您执行以下操作,则应保留派生类(b、c、d 等)的方法。

List<B> blist = new List<B>(); 
List<C> cList = new List<C>(); 
List<D> dList = new List<D>();  
List<List<A>> arrays = new List<List<A>>();

【讨论】:

    猜你喜欢
    • 2020-02-15
    • 2015-08-03
    • 2020-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-29
    • 2012-10-19
    相关资源
    最近更新 更多