【问题标题】:How to specialize an inherited List<SuperClass> to a List<SubClass> in C#?如何在 C# 中将继承的 List<SuperClass> 专门化为 List<SubClass>?
【发布时间】:2014-08-14 17:58:48
【问题描述】:

这听起来像是一个常见的问题,所以如果你有一个基类 A 和 List&lt;SuperClass&gt; 类型的公共属性,并且你在 B 类中继承了该列表,但想要使用更专业的类型参数,那么最佳实践是什么?列表:

class SuperClass 
{
    public bool Flag;
}

class SubClass : SuperClass 
{
    public int Number;
}

class A
{
    public List<SuperClass> Elements { get; }
}

class B : A
{
   public List<SubClass> Elements { get; set; }
}

那么如何用一个新列表覆盖 Elements 列表,但仍然确保如果另一个类只知道 A 对象,它会被访问?

class C
{
    List<A> AList;

    void FillList()
    {
        AList.Add(new B());
    }

    void DoSomething()
    {
        foreach (var a in AList)
            forach(var super in a.Elements)
                super.Flag = true;  
    }
}

【问题讨论】:

  • 你不能覆盖它,因为它们是不同的类型。您是否正在寻找 new 来隐藏基本实现? public new List&lt;SubClass&gt; Elements { get; set; }
  • 这里有一篇关于这个主题的好帖子:stackoverflow.com/questions/2231668/…
  • 看起来像一个设计问题。当您想将 B 类已知的项目放入该列表中时,为什么要创建 List

标签: c# .net inheritance collections


【解决方案1】:

我试了一下。

class Program {
    static void Main(string[] args) {

        var c = new C();
        c.FillList();
        c.DoSomething();


        Console.ReadKey();

    }
}


class C {
    List<A> AList;

    public void FillList() {
        AList = new List<A>();

        var a = new A();
        a.Elements = new List<SuperClass>() { new SubClass(), new SuperClass() };
        AList.Add(a);

        var b = new B();
        b.Elements = new List<SubClass>() { new SubClass(), new SubClass() };
        AList.Add(b);
    }

    public void DoSomething() {
        foreach (var a in AList)
            foreach (var super in a.Elements) { 
                super.Flag = true;
                Console.WriteLine(super.GetName());
            }
    }
}


class SuperClass {
    public bool Flag;
    public virtual string GetName() { return "super"; } 
}
class SubClass : SuperClass {
    public SubClass() { }
    public SubClass(SuperClass x) { }

    public int Number;
    public override string GetName() { return "sub"; } 
}

class A {

    public virtual IEnumerable<SuperClass> Elements {
        get{
            return elementList.AsEnumerable();
        }
        set {
            elementList = value.ToList();
        }
    }

    private List<SuperClass> elementList;
}

class B : A {

    public override IEnumerable<SuperClass> Elements {
        get {
            return elementList.AsEnumerable();
        }
        set {
            elementList = value.Aggregate(new List<SubClass>(), 
                                (acc, x) => {
                                    if (x is SubClass) 
                                        acc.Add((SubClass)x);
                                    else 
                                        acc.Add(new SubClass(x)); 
                                    return acc; });
        }
    }

    private List<SubClass> elementList;
}

我使用 IEnumerable。 并在调用 setter 属性(B 类)时将 IEnumerable&lt;SuperClass&gt; 转换为 List&lt;SubClass&gt;

【讨论】:

  • 这看起来不错,我也做到了这一点,但 IEnumerable 在我的场景中还不够,我需要使用索引器进行随机访问,并且还需要能够对原始列表进行排序。
【解决方案2】:

你不能。因为就目前而言,如果通过 A 接口插入 SuperClass,B 会失败,因为 SuperClass 无法插入 B 的 SubClass 列表中。

你应该用谷歌搜索的具体术语是CovarianceContravariance

如果您将类限制为至少在基类中进行读取访问,则可以解决您的情况。然后,您的 B 类中可以有两个只读属性,一个是专用的,一个不是专用的,但返回专用版本。

【讨论】:

  • 是的,但在这种情况下,最佳做法是什么?我可以从 List 构造一个 ReadOnlyList 并发布这个吗?
  • @thalm 最佳实践是什么?您能否举例说明您的场景是什么以及您想如何使用它?当然,您可以使用 ReadOnlyList,但这与当前列表没有任何联系。或者您可以使用 nvoigt 所写的协方差和逆变,并使用 inout 参数。但是“最佳实践”在某种程度上取决于您将如何使用它
  • 在这种特殊情况下,只知道 A 和 SuperClass 的类需要对列表进行读取访问,并且还需要通过 SuperClass 的 float 属性对列表进行排序。
猜你喜欢
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
  • 2019-02-17
  • 2016-04-06
  • 1970-01-01
相关资源
最近更新 更多