具有通用列表的类继承设计

时间:2018-05-07 18:47:54

标签: c#

我目前正在努力进行涉及通用列表的类设计:

代码说的超过千言万语:

class Document
{
    public List<Result> Results { get; } = new List<Result>();
}

class Result
{
}

class SpecialDocument : Document
{
    public new List<SpecialResult> Results { get; } = new List<SpecialResult>();
}

class SpecialResult : Result
{
}

我不喜欢当前的设计是SpecialDocument.Results隐藏Document.Results。如果在Document上有SpecialDocument视图,则根本没有结果,即使所有Result元素都有SpecialResult视图。

我想要完成的是:

SpecialDocument doc = new SpecialDocument();
doc.Results.Add(new SpecialResult());
Assert.AreEqual(1, (doc as Document).Results.Count); // Here my design obviously fails right now

...我想在不失去类型安全的情况下实现这一目标(因为这实际上是List<T>不协变的原因。)

修改

我忘了提及SpecialDocumentDocument实际上需要具有相同的后继(或实现相同的接口),以便它们可以在一个集合中共存:

List<Document> documents = new List<Document>()
{
    new Document(),
    new SpecialDocument()
};

2 个答案:

答案 0 :(得分:4)

你尝试过泛型吗?

abstract class Document<T> where T : Result
{
    public List<T> Results { get; } = new List<T>();
}

abstract class Result
{
}

class SpecialDocument : Document<SpecialResult>
{
}

class SpecialResult : Result
{
}

SpecialDocument会自动实例化List<SpecialResult>

答案 1 :(得分:1)

如果IDocument.Results可以接受IEnumerable<Result>类型而不是IList<Result>,则可以使用。

由于协方差/反差方法规则,Result属性必须以这种方式输入。

代码基于@Kyle B的优秀答案

void Main()
{

    IDocument doc = new SpecialDocument();

    //I added AddResult() to the interface to allow adding results, instead of calling Add() directly on the list.
    doc.AddResult(new SpecialResult());
    Assert.AreEqual(1, doc.Results.Count);

    // prooving that the items can be added to a list, and that list can handle all the result types.
    var docs = new List<IDocument>();
    docs.Add(new Document());
    docs.Add(new SpecialDocument());
    var results = docs.SelectMany(d => d.Results)
    // results now contains all results from all documents
}

abstract class Document<T> : IDocument where T : Result
{
    public List<T> Results { get; } = new List<T>();

    IEnumerable<Result> IDocument.Results => Results;

    void IDocument.AddResult(Result result)
    {
        this.Results.Add((T)result);
    }
}

abstract class Result
{
}

class Document : Document<Result>
{
}

class SpecialDocument : Document<SpecialResult>
{
}

class SpecialResult : Result
{
}

interface IDocument
{
    IEnumerable<Result> Results { get; }
    AddResult(Result result);
}