首页 > 解决方案 > 如何在复合 C# 中继承多个叶子部分?

问题描述

我正在用 C# 编写项目,其中包含不止一个叶子,我想知道这些节点是否应该继承复合而不是抽象类。所以我有三片叶子:

  1. 段落
  2. 段落组
  3. 文档

段落继承文本类,它是抽象类。现在,Group of Paragraphs 类继承了 Paragraph 类,而 Document 继承了 Group of Paragraphs 和 Paragraphs。所以我想知道哪个类应该是我的复合材料?我正在考虑将复合材料放在 ParagraphComposite.cs 类中,然后让它看起来像这样:

Paragraph 和 ParagraphComposite 继承抽象类 text 和 Group of Paragraphs 和 Document 继承 ParagraphComposite 类。

这是正确的方法吗?任何反馈将不胜感激。

标签: c#composite

解决方案


让我们假设所有这些类都具有共同的属性,例如Text属性,但也有不同的属性,例如其他类型的集合。为了不与Text属性冲突,我将调用抽象基类TextBase

其他类应派生自TextBase. 例如,Document文档将有一个ParagraphGroups集合,但没有一个Paragraphs集合。即,它不能派生自ParagraphGroup哪一个Paragraphs集合。

可能的方法:

public abstract class TextBase
{
    public const string LineSeparator = "\n";
    public const string ParagraphSeparator = "\r";
    public const string ParagraphGroupSeparator = "\v";

    public abstract string Text { get; set; }
}
public class Paragraph : TextBase
{
    public string[] Lines { get; set; } = new string[0];

    public override string Text
    {
        get => String.Join(LineSeparator, Lines);
        set => Lines = value.Split(new string[] { LineSeparator }, StringSplitOptions.None);
    }
}
public class ParagraphGroup : TextBase
{
    public Paragraph[] Paragraphs { get; set; } = new Paragraph[0];

    public override string Text
    {
        get => String.Join(ParagraphSeparator, Paragraphs.Select(p => p.Text));
        set => Paragraphs =
            value.Split(new string[] { ParagraphSeparator }, StringSplitOptions.None)
                .Select(s => new Paragraph { Text = s })
                .ToArray();
    }
}
public class Document : TextBase
{
    public ParagraphGroup[] ParagraphGroups { get; set; } = new ParagraphGroup[0];

    public override string Text
    {
        get => String.Join(ParagraphGroupSeparator, ParagraphGroups.Select(g => g.Text));
        set => ParagraphGroups =
            value.Split(new string[] { ParagraphGroupSeparator }, StringSplitOptions.None)
                .Select(s => new ParagraphGroup { Text = s })
                .ToArray();
    }
}

该类Document不继承ParagraphGroup。相反,它包含一个ParagraphGroup. 为了简单起见,我使用了数组,但您也可以使用List<T>或创建自己的特定集合类型。

请注意,当您获取或设置 的Text属性时Document,这会递归调用 and 的属性Text,以便递归地连接或拆分元素。ParagraphGroupParagraph

您将获得文档中的第一行文本

string firstLine = document.ParagraphGroups[0].Paragraphs[0].Lines[0];

你在哪里放置类似Addor的方法Remove

一种可能性是简单地依赖您正在使用的集合的相应方法。示例:如果您使用的是List<T>集合,则可以简单地使用列表方法(例如paragraph.Lines.Add("hello");.

如果您想将这些方法直接添加到DocumentParagraph,您将面临其中一些将采用不同类型的参数的问题。你可以TextBase通用,但你会失去分配的兼容性。最好声明一个泛型接口。例如

public interface ITextCollection<T>
{
    public abstract void Add(T item);
    public abstract void RemoveAt(int i);
}

一个可能的实现Paragraph(假设您使用的是List<string>forLines属性。Paragraph将实现ITextCollection<string>

public void Add(string item)
{
    Lines.Add(item);
}

public void RemoveAt(int i)
{
    Lines.RemoveAt(i);
}

请注意,其他类型的实现是不同的,因为它们的集合名称不同并且属于另一种类型。例如ParagraphGroup,将实施ITextCollection<Paragraph>并且必须从Paragraphs集合中添加和删除。

这使得您的类组合起来,因为它们本身不实现集合功能的细节,而是包含一个集合并将操作委托给这些集合。


推荐阅读