C# 把所有的孩子都放在树上

C# 把所有的孩子都放在树上,c#,C#,有一个类存储元素树。子元素存储在 public List<BaseTreeData> Child { get; set; } 我想将此树显示为所有元素的平面线性列表。在将类划分为两个base和heir之后,GetChildren方法会生成一个关于类型不匹配的错误。很可能一切都合乎逻辑,但如何解决呢 错误CS1503参数1:无法从“ConsoleApplication1.BaseTreeData”转换为“ConsoleApplication1.TreeData” 错误CS1503参数

有一个类存储元素树。子元素存储在

public List<BaseTreeData> Child { get; set; }
我想将此树显示为所有元素的平面线性列表。在将类划分为两个base和heir之后,GetChildren方法会生成一个关于类型不匹配的错误。很可能一切都合乎逻辑,但如何解决呢

错误CS1503参数1:无法从“ConsoleApplication1.BaseTreeData”转换为“ConsoleApplication1.TreeData”

错误CS1503参数1:无法从“ConsoleApplication1.BaseTreeData”转换为“ConsoleApplication1.TreeData”

发生此错误的原因是子节点是BaseTreeData而不是TreeData

使用您发布的BaseTreeData类定义,子级和父级将始终返回基类型

相反,您可以使用泛型解决此问题,以便子节点的类型与父类的类型相同:

class BaseTreeData<T> where T : BaseTreeData<T>
{
    public bool IsChecked { get; set; }
    public T Parent { get; set; }
    public List<T> Children { get; set; }

    public BaseTreeData()
    {
        Children = new List<T>();
    }

    public IEnumerable<T> GetAncestors()
    {
        if (Parent == null)
            yield break;

        T relative = Parent;
        while (relative != null)
        {
            yield return relative;
            relative = relative.Parent;
        }
    }

    public IEnumerable<T> GetDescendants()
    {
        var nodes = new Stack<T>();
        nodes.Push(this as T);

        while (nodes.Any())
        {
            var current = nodes.Pop();
            yield return current;

            foreach (var childNode in current.Children)
                nodes.Push(childNode);
        }
    }
}

class TreeData : BaseTreeData<TreeData>
{
    public int ID { get; set; }
    public string Name { get; set; }
}
错误CS1503参数1:无法从“ConsoleApplication1.BaseTreeData”转换为“ConsoleApplication1.TreeData”

发生此错误的原因是子节点是BaseTreeData而不是TreeData

使用您发布的BaseTreeData类定义,子级和父级将始终返回基类型

相反,您可以使用泛型解决此问题,以便子节点的类型与父类的类型相同:

class BaseTreeData<T> where T : BaseTreeData<T>
{
    public bool IsChecked { get; set; }
    public T Parent { get; set; }
    public List<T> Children { get; set; }

    public BaseTreeData()
    {
        Children = new List<T>();
    }

    public IEnumerable<T> GetAncestors()
    {
        if (Parent == null)
            yield break;

        T relative = Parent;
        while (relative != null)
        {
            yield return relative;
            relative = relative.Parent;
        }
    }

    public IEnumerable<T> GetDescendants()
    {
        var nodes = new Stack<T>();
        nodes.Push(this as T);

        while (nodes.Any())
        {
            var current = nodes.Pop();
            yield return current;

            foreach (var childNode in current.Children)
                nodes.Push(childNode);
        }
    }
}

class TreeData : BaseTreeData<TreeData>
{
    public int ID { get; set; }
    public string Name { get; set; }
}
基类属性子对象的类型为List,但您正在尝试调用静态方法GetChildren,该方法需要TreeData类型的对象。您实际上是在尝试向上投射对象。编译器如何知道用什么填充ID和名称

更优雅的方法是让每个类决定其字符串表示形式。这样就不需要GetChildren方法,因为您可以只使用基类的Child属性:

foreach (var item in data.SelectMany(x => x.Child))
{
    Console.WriteLine(item.ToString());
}
然后重写ToString实现,以便基类提供其值,派生类在此基础上构建:

class BaseTreeData
{
    //Other stuff here
    //...
    public override string ToString()
    {
        return IsChecked.ToString();
    }
}

class TreeData : BaseTreeData
{
    //Other stuff here
    //...
    public override string ToString()
    {
        var format = "{0} {1} {2}";
        var stringRepresentation = string.Format(format, ID, Name, base.ToString());
        return stringRepresentation;
    }
}
请注意参数中对base.ToString的调用

输出:

10错误

11错误

基类属性子对象的类型为List,但您正在尝试调用静态方法GetChildren,该方法需要TreeData类型的对象。您实际上是在尝试向上投射对象。编译器如何知道用什么填充ID和名称

更优雅的方法是让每个类决定其字符串表示形式。这样就不需要GetChildren方法,因为您可以只使用基类的Child属性:

foreach (var item in data.SelectMany(x => x.Child))
{
    Console.WriteLine(item.ToString());
}
然后重写ToString实现,以便基类提供其值,派生类在此基础上构建:

class BaseTreeData
{
    //Other stuff here
    //...
    public override string ToString()
    {
        return IsChecked.ToString();
    }
}

class TreeData : BaseTreeData
{
    //Other stuff here
    //...
    public override string ToString()
    {
        var format = "{0} {1} {2}";
        var stringRepresentation = string.Format(format, ID, Name, base.ToString());
        return stringRepresentation;
    }
}
请注意参数中对base.ToString的调用

输出:

10错误

11错误



Eric Lippert有一个非常有用的遍历方法,非常适合获得这样的节点后代:@MongZhu是的,你完全正确。这就是课程需要改变的原因。您希望能够执行treenode.Parent.Parent.Children,并且仍然具有正确的派生类型。@您完全正确。我不再那么确定了。答案中的MyNode是我的BaseTreeData还是TreeData?@egeo该示例是一个扩展方法,可能不适合这里。由于这是您控制的类,因此可以直接在BaseTreeData类上创建方法。但是,如果您实现了泛型,则类型应该是T。Eric Lippert提供了一种非常有用的遍历方法,非常适合获取如下节点的后代:@MongZhu是的,您完全正确。这就是课程需要改变的原因。您希望能够执行treenode.Parent.Parent.Children,并且仍然具有正确的派生类型。@您完全正确。我不再那么确定了。答案中的MyNode是我的BaseTreeData还是TreeData?@egeo该示例是一个扩展方法,可能不适合这里。由于这是您控制的类,因此可以直接在BaseTreeData类上创建方法。但是,如果你实现了泛型,那么类型应该是T。如果我像你说的那样只更改类的描述,“我得到一个错误,进程因StackOverflowException而终止。@egeo请查看我对您如何解决该问题的链接注释。@egeo我添加了如何在结构中向上/向下遍历的示例。我尝试了您的方法,因此var result=data.SelectManyx=>x.GetDescents.ToList;-好好工作@egeo即使这样也应该起作用:var result=data.getsubstands.ToList;如果我像你说的那样只更改类的描述,“我得到一个错误,进程因StackOverflowException而终止。@egeo请查看我对您如何解决该问题的链接注释。@egeo我添加了如何在结构中向上/向下遍历的示例。我尝试了您的方法,因此var result=data.SelectManyx=>x.GetDescents.ToList;-好好工作@egeo即使这样也应该起作用:var result=data.getsubstands.ToList;更多的元素
蚂蚁的方法是什么,写控制台?从根本上说,这是一种树形结构,摆脱GetChildren似乎不是一个实际的解决方案。@ParrishHurn:这当然是一种比试图打印出你认为对象的样子更面向对象的方法。你的代码不应该做出假设。我不同意你关于类型错误的评论。我想我们只是在实际问题所在上存在分歧。由于在OPs示例中treeNode.Parent和treeNode.Children没有返回相同的派生类型,因此该类已被破坏。@Parrishharn:顺便说一下,这些基本上都是对象。OP正在尝试获取它们的字符串表示形式。这就是ToString方法的用途,这就是为什么它是可重写的,所以你可以提供你自己的版本,你认为它应该是什么样子。人们总是可以用谷歌搜索它。话虽如此,我强烈建议代码完整。我读过的最好的一本书:一个更优雅的方法,写什么,安慰?从根本上说,这是一种树形结构,摆脱GetChildren似乎不是一个实际的解决方案。@ParrishHurn:这当然是一种比试图打印出你认为对象的样子更面向对象的方法。你的代码不应该做出假设。我不同意你关于类型错误的评论。我想我们只是在实际问题所在上存在分歧。由于在OPs示例中treeNode.Parent和treeNode.Children没有返回相同的派生类型,因此该类已被破坏。@Parrishharn:顺便说一下,这些基本上都是对象。OP正在尝试获取它们的字符串表示形式。这就是ToString方法的用途,这就是为什么它是可重写的,所以你可以提供你自己的版本,你认为它应该是什么样子。人们总是可以用谷歌搜索它。话虽如此,我强烈建议代码完整。我读过的最好的书之一: