如何构造一个对象来表示主题列表?

How to struct an object to represent a list of topics?

我正在使用 C# 进行编码,我正在尝试制作主题列表的 OOP 表示。我尝试了无数方法,但仍然无法达到预期的结果。

我想稍后制作一个输出如下的方法:

1) Text
  1.1) Text
2) Text
  2.1) Text
  2.2) Text
    2.2.1) Text
    2.2.2) Text
  2.3) Text
3) Text
  3.1) Text

当需要获取单个主题时,我想创建一个调用我的对象的方法,例如:

private string GetSingleTopic()
{
  return $"{Topic.Numerator}) {Topic.Text}"
}

例子

示例 1

我将能够实例化对象,例如:

var variable = new TopicObject
{
  "TitleA",
  "TitleB",
  "TitleC" 
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
   --- OUTPUT --- */

示例 2

能够实例化对象如:

var variable = new TopicObject
{
  "TitleA",
  "TitleB",
  "TitleC":
  {
    "TitleD":
    {
      "TitleE"
    },
    "TitleF":
    {
      "TitleG",
      "TitleH"
    }
  }
}
/* --- OUTPUT ---
1) TitleA
2) TitleB
3) TitleC
  3.1) TitleD
    3.1.2) TitleE
  3.2) TitleF
    3.2.1) TitleG
    3.2.2) TitleH
   --- OUTPUT --- */

我的方法

这是我的众多方法之一。我无法使用它,因为我无法按照我提到的方式初始化内部主题列表,例如层次结构。 但结构与我想要实现的非常相似,所以我决定放在这里作为示例。

public abstract class TopicBase
{
    public List<Topic> Topics { get; set; } // optional

    protected TopicBase() { Topics = new List<Topic>(); }
    protected TopicBase(List<Topic> topics) { Topics = topics; }

    public TopicBase AddTopic(string topicText)
    {
        var test = new Topic(topicText);
        Topics.Add(test);
        return this;
    }
}

public class Topic
{
    public Topic(string text)
    {
        Numerator++;
        Text = text;
    }
    public int Numerator { get; }
    public string Text { get; }
}

public class TopicLevel1 : TopicBase { }
public class TopicLevel2 : TopicBase { }
public class TopicLevel3 : TopicBase { }

如果目标是一些 有助于输出主题层次结构的结构,您已经拥有它(甚至可以进一步简化它)。

例如,这里有一个几乎-最小Topic来得到你想要的:

public class Topic
{
    public string Title { get; set; }
    public List<Topic> SubTopics { get; private set; } = new();

    public Topic() : this("DocRoot") { }

    public Topic(string title) => Title = title;

    public void AddTopics(List<Topic> subTopics) => SubTopics.AddRange(subTopics);

    public void AddTopics(params Topic[] subTopics) => SubTopics.AddRange(subTopics);

    public override string ToString() => Title;
}

也就是说,您有一个 Topic 可以有 SubTopics(又名 children),这就是您 所需要的

这样,我们就可以构建您的第二个示例:

var firstLevelTopics = new List<Topic>();
for (var c = 'A'; c < 'D'; ++c)
{
    firstLevelTopics.Add(new Topic(c.ToString()));
}

var cTopic = firstLevelTopics.Last();
cTopic.AddTopics(
    new Topic
    {
        Title = "D",
        SubTopics = { new Topic("E") }
    },
    new Topic
    {
        Title = "F",
        SubTopics = { new Topic("G"), new Topic("H") }
    });

现在,想象一下,如果我们有一个函数来打印 top-level 主题列表中的层次结构。我将把最后的细节留给你自己,以防这是作业。

PrintTopics(firstLevelTopics);
static void PrintTopics(List<Topic> topics, string prefix = "")
{
    // For the simple case, we can just loop and print...
    for (var i = 0; i < topics.Count; ++i)
    {
        var topic = topics[i];
        var level = i + 1;
        Console.WriteLine($"{prefix}{level}) {topic}");
        // ...but, if we want to print the children, we need more.
        // Make a recursive call to print the SubTopics
        // PrintTopics(<What goes here?>);
    }
}

让我们从定义一个可以容纳主题的数据结构开始:

public class Topics<T> : List<Topic<T>> { }

public class Topic<T> : List<Topic<T>>
{
    public T Value { get; private set; }
    public Topic(T value, params Topic<T>[] children)
    {
        this.Value = value;
        if (children != null)
            this.AddRange(children);
    }
}

这让我们可以编写这段代码:

var topics = new Topics<string>()
{
  new Topic<string>("TitleA"),
  new Topic<string>("TitleB"),
  new Topic<string>("TitleC",
    new Topic<string>("TitleD",
        new Topic<string>("TitleE")),
    new Topic<string>("TitleF",
        new Topic<string>("TitleF"),
        new Topic<string>("TitleH")))
};

与您的“示例 2”数据匹配。

为了输出结果,我们添加了两个 ToOutput 方法。

Topics<T>

public IEnumerable<string> ToOutput(Func<T, string> format)
    => this.SelectMany((t, n) => t.ToOutput(0, $"{n + 1}", format));

Topic<T>

public IEnumerable<string> ToOutput(int depth, string prefix, Func<T, string> format)
{
    yield return $"{new string(' ', depth)}{prefix}) {format(this.Value)}";
    foreach (var child in this.SelectMany((t, n) => t.ToOutput(depth + 1, $"{prefix}.{n + 1}", format)))
    {
        yield return child;
    }
}

现在我可以运行这个代码:

foreach (var line in topics.ToOutput(x => x))
{
    Console.WriteLine(line);
}

这给了我:

1) TitleA
2) TitleB
3) TitleC
 3.1) TitleD
  3.1.1) TitleE
 3.2) TitleF
  3.2.1) TitleF
  3.2.2) TitleH