我正在使用一棵树,它包含两种不同类型的 node ,内部 node 和外部 node .我希望内部 node 类中的成员‘leftChild’和‘rightChild’既可以指向内部 node ,也可以指向外部 node .有什么办法可以做到这一点吗?

我try 创建一个基类,并从基类继承两种类型的 node ,这是可行的,但当我从树中导航到它时,我无法访问子 node 的任何单个成员.

public abstract class Node {
    public Node parent;
}
public class InnerNode : Node {
    private Node leftChild, rightChild;
    private int someInnerNodeMember;
}
public class OuterNode : Node {
    private float someOuterNodeMember;
}

我目前唯一的解决方案是让每个类的每个子成员都有两次,并将不习惯的设置为空,这会使事情变得复杂得多.

public class InnerNode : Node {
    private InnerNode leftChildAsInner, rightChildAsInner;
    private OuterNode leftChildAsOuter, rightChildAsOuter;
    private int someInnerNodeMember;
}
public class OuterNode : Node {
    private float someOuterNodeMember;
}

我对C#比较陌生,来自C语言,所以我可能不熟悉一些非常基本的机制,但如果是这样的话,我也找不到任何有用的东西.

推荐答案

据我所知,这里需要multiple dispatch,也就是说,如果每种类型有两个方法,则动态决定调用哪个方法.

TL;DR访问者模式、动态关键字或switch 盒.在任何情况下,您都需要 for each 子类型单独编写函数块(在此块或函数中,您将拥有具体子类型,因此可以访问其所有成员).

在实践中,我会说切换 case 的方式是使用最多的,动态的我认为几乎从来没有.

访客

其中一个解决方案是在这里使用访客 pattern.您需要为您使用的每个子类型使用一个单独的方法,然后可以像这样使用它.

public abstract class Node
{
    public Node parent;
    public abstract void Accept(访客 visitor);
}
public class InnerNode : Node
{
    internal Node leftChild, rightChild;
    internal int someInnerNodeMember;

    public override void Accept(访客 visitor)
    {
        visitor.Visit(this);
    }
}
public class OuterNode : Node
{
    internal float someOuterNodeMember;


    public override void Accept(访客 visitor)
    {
        visitor.Visit(this);
    }
}

public class 访客
{
    public void Visit(InnerNode inner)
    {
        Console.WriteLine($"Inner node {inner.someInnerNodeMember}" );
    }

    public void Visit(OuterNode outer)
    {
        Console.WriteLine($"Outer node {outer.someOuterNodeMember}");

    }
}
public class Program
{
    public static void Main()
    {
        var visitor = new 访客();
        var inner = new InnerNode
        {
            leftChild = new InnerNode { someInnerNodeMember = 1 },
            rightChild = new OuterNode { someOuterNodeMember = 12 }
        };

        inner.leftChild.Accept(visitor);
        inner.rightChild.Accept(visitor);
    }
}

访问者基本上是您应用逻辑的地方,所以如果您希望InnerNode是能够区分内部和外部 node 类型的地方,那么它也是访问者,您可以将访问器方法移动到内部 node 并重写程序,如下所示

public abstract class Node
{
    public Node parent;
    public abstract void Accept(I访客 visitor);
}
public class InnerNode : Node, I访客
{
    internal Node leftChild, rightChild;
    internal int someInnerNodeMember;

    public void PrintChildrenDetails()
    {
        leftChild?.Accept(this);
        rightChild?.Accept(this);
    }

    public override void Accept(I访客 visitor)
    {
        visitor.Visit(this);
    }

    public void Visit(InnerNode inner)
    {
        Console.WriteLine($"Inner node {inner.someInnerNodeMember}");
    }

    public void Visit(OuterNode outer)
    {
        Console.WriteLine($"Outer node {outer.someOuterNodeMember}");

    }
}
public class OuterNode : Node
{
    internal float someOuterNodeMember;


    public override void Accept(I访客 visitor)
    {
        visitor.Visit(this);
    }
}

public interface I访客
{
    void Visit(InnerNode inner);
    void Visit(OuterNode inner);

}

public class Program
{
    public static void Main()
    {
        var inner = new InnerNode
        {
            leftChild = new InnerNode { someInnerNodeMember = 1 },
            rightChild = new OuterNode { someOuterNodeMember = 12 }
        };

        inner.PrintChildrenDetails();
    }
}

动态

使用动态关键字也可以获得相同的结果,但不推荐使用(它有性能问题e.g. see here)

public abstract class Node
{
    public Node parent;
}
public class InnerNode : Node
{
    internal Node leftChild, rightChild;
    internal int someInnerNodeMember;

    public void PrintChildrenDetails()
    {
        Process((动态)leftChild);
        Process((动态)rightChild);
    }

    public void Process(InnerNode inner)
    {
        Console.WriteLine($"Inner node {inner.someInnerNodeMember}");
    }

    public void Process(OuterNode outer)
    {
        Console.WriteLine($"Outer node {outer.someOuterNodeMember}");

    }
}
public class OuterNode : Node
{
    internal float someOuterNodeMember;
}



public class Program
{
    public static void Main()
    {
        var inner = new InnerNode
        {
            leftChild = new InnerNode { someInnerNodeMember = 1 },
            rightChild = new OuterNode { someOuterNodeMember = 12 }
        };

        inner.PrintChildrenDetails();
    }
}

切换表达式

只需使用Switch表达式并处理每种类型用例.

public abstract class Node
{
    public Node parent;
}
public class InnerNode : Node
{
    internal Node leftChild, rightChild;
    internal int someInnerNodeMember;

    public void PrintChildrenDetails()
    {
        Process(leftChild);
        Process(rightChild);
    }

    public void Process(Node node)
    {
        switch (node)
        {
            case InnerNode inner:
                Console.WriteLine($"Inner node {inner.someInnerNodeMember}");
                break;
            case OuterNode outer:
                Console.WriteLine($"Outer node {outer.someOuterNodeMember}");
                break;
        }
    }
}
public class OuterNode : Node
{
    internal float someOuterNodeMember;
}

public class Program
{
    public static void Main()
    {
        var inner = new InnerNode
        {
            leftChild = new InnerNode { someInnerNodeMember = 1 },
            rightChild = new OuterNode { someOuterNodeMember = 12 }
        };

        inner.PrintChildrenDetails();
    }
}


Csharp相关问答推荐

等待限制选项似乎不适用于频道

错误NU 1301:无法加载源的服务索引

如何告诉自己创建的NuGet包在应用程序中发生了变化?

使用LayoutKind在C#中嵌套 struct .显式

Unity 2D自顶向下弓旋转

. net依赖注入如何避免服务类中的新

如何注册接口类型,类型<>

Automapper 12.x将GUID映射到字符串

VS 2022 for ASP.NET Core中缺少自定义项模板

在两个已具有一对多关系的表之间添加另一个一对多关系

在Docker容器中运行API项目时,无法本地浏览到index.html

我什么时候应该在Dapper中使用Connection.OpenAsync?

如何在microsoft.clearscript.v8的jsondata中使用Linq

在';、';附近有错误的语法.必须声明标量变量";@Checkin";.';

在.NET8中如何反序列化为私有字段?

岛屿和框架中的自定义控件库.Navigate-AccessViolationException

ASP.NET核心8:app.UseStaticFiles()管道执行顺序

在Visual Studio 2022中查找Xamarin模板时遇到问题

如何在更新数据库实体时忽略特定字段?

实例化列表时的集合表达式是什么?