【问题标题】:Is there any way to access fields of 'super' class (not inherited)有什么方法可以访问“超级”类的字段(不继承)
【发布时间】:2016-05-10 18:53:09
【问题描述】:

标题可能会产生误导,因为我在搜索甚至创建正确问题时遇到了一些麻烦,所以让我给出一个我正在努力解决的真正问题:

我有一个Graph 课程。由于图形需要节点和边,我创建了两个额外的类Node(顶点)和Edge。我的结构如下所示:

class Graph
{
    List<Node> nodes;
    List<Edge> edges;

    public Graph( ... ) { /* populate lists */ }
}

class Node { ... }
class Edge { ... }

我为Node 类写了一些方法,其中一个对我来说特别成问题。签名:

public List<Node> GetNeighbours(List<Edge> edges) { ... }

相当标准。给定一张图,我问一个节点:你有多少个邻居?我需要边缘列表来解决它。

如何重构此代码,以便可以在内部使用 Graph 属性/字段,而不是每次都传递边列表?这样的事情可能吗:

public List<Node> GetNeighbours()
{
    // ...
    foreach(edge in *BASE*.edges) { ... }
}

我知道我不能使用 base 关键字,因为我不想在这里进行任何继承(为什么节点必须从图形继承?!)并且嵌套类似乎也对我没有帮助(不访问“父母的”字段)。

这段代码现在可以运行,但我觉得它不够优雅,我想体验一下合适的解决方案。

【问题讨论】:

  • 您需要传递“父”类本身。
  • @SLaks 所以基本上我需要在Node 类中创建额外的Graph?不可能做到“图表是我的父母,我想要他的边缘”?感谢您的回复。
  • 在构造图的时候怎么样,每个节点都会包含一个对连接到它的每条边的引用,而每条边都会对两端的节点有一个引用。你不需要做所有这些。

标签: c# class inheritance inner-classes


【解决方案1】:

Graph 构造函数中传递对父类的引用。

类似:

class Graph
{
    private ParentType parent;

    public void Graph(ref ParentType parent)
    {
        this.parent = parent;
    }
}

然后,在GetNeighbours 方法中(假设ParentType 有一个Edges 集合属性):

public List<Node> GetNeighbours()
{
    // ...
    foreach(var edge in parent.Edges) { ... }
}

【讨论】:

  • 哇,这成功了。当我使用Graph 方法时,它修改了ParentType(准确地说是此类的对象)字段。我不知道它是通过引用传递的(缺少ref 关键字)。
  • @rsqLVo - 除非使用 ref 关键字,否则它不会通过引用传递。它是按值传递的,如果是引用类型,则传递引用的副本。
  • 不错的 @ChrisDunaway,已修复。
【解决方案2】:

根据您正在尝试执行的操作的描述:

给定一个图,我问一个节点:你有多少个邻居?

您确定这应该是Node 的方法吗?由于Graph 包含NodesEdges 可能这种方法在Graph 中更好。

public List<Node> GetNeighbours(Node node)
{
    if(!nodes.Contains(node)
    {
        return new List<Node>(); //No neighbors. Return an empty list.
    }
    // Find and return the neighbors. This method is in Graph so it
    // has access to all of Graph's internals.
}

我的推理是,因为在某种意义上Graph 是一个父对象并且它包含Nodes,所以Node 不需要知道Graph。它的目的(单一职责)是完整的,没有对Graph 的任何引用。

【讨论】:

  • 这很好。我的方法是出于对特定Node 着色的需要,所以从更广泛的角度来看,我在看“我想变红,但我需要先检查我的邻居”:) 感谢您的回答!
【解决方案3】:

我会在Graph 上使用Graph.AddNodes()Graph.AddEdges() 之类的方法,以便这是确保所有Nodes(和/或Edges)都具有所需参考的中心位置。我在想这样的事情,取决于你的NodeEdge的型号。

class Graph
{
    List<Node> nodes;
    List<Edge> edges;

    public Graph( ... ) { /* populate lists */ }

    public void AddEdges(params Edge[] edges) {
        foreach (var edge in edges) {
            edge.Node1.Parent = this;
            edge.Node2.Parent = this;
        }
    }
}

class Node {
    public Graph Parent { get; set; }

    public List<Node> GetNeighbours()
    {
        var neighbors = new List<Node>();
        foreach(var edge in parent.Edges) { 
            if (edge.Node1 == this && !neighbors.Contains(edge.Node2)) {
                neighbors.Add(edge.Node2);
            }
            else if (edge.Node2 == this && !neighbors.Contains(edge.Node1)) {
                neighbors.Add(edge.Node1);
            }
        }
    }
}

class Edge {
    public Node Node1 { get; set; }
    public Node Node2 { get; set; }
}

【讨论】:

  • 想指出,这种方法具有相当高的时间复杂度(循环遍历图的所有边 + 2 包含对邻居列表的调用)。类似于 O(n-squared)。
  • 我应该使用HashSet,所以我不需要Contains 方法
【解决方案4】:

这是另一种方法。您可以让每条边都知道每一端的节点,而不是传递父引用。并让每个节点都知道连接到它们的边。

这样做的一个巨大优势是您不需要枚举可能大量的节点/边缘来找到您需要的东西。您已经拥有所需的一切,因此速度要快得多。

这是我描述的方法的快速示例以及一些测试:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GraphModelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            TestA();
            TestB();
            TestC();
        }

        private static void TestC()
        {
            //A <-> B
            //|     |
            //D <-> C
            Node a = new Node("a");
            Node b = new Node("b");
            Node c = new Node("c");
            Node d = new Node("d");
            Edge ab = a.ConnectTo(b);
            Edge bc = b.ConnectTo(c);
            Edge cd = c.ConnectTo(d);
            Edge da = d.ConnectTo(a);
            Graph g = new Graph();
            g.Nodes.Add(a);
            g.Nodes.Add(b);
            g.Nodes.Add(c);
            g.Nodes.Add(d);
            g.Edges.Add(ab);
            g.Edges.Add(bc);
            g.Edges.Add(cd);
            g.Edges.Add(da);
            Console.WriteLine(g.ToString());

            Console.WriteLine("Neighbours of B");
            foreach (Node n in b.GetNeighbours())
            {
                Console.WriteLine(n.ToString());
            }
            Console.WriteLine("Neighbours of D");
            foreach (Node n in d.GetNeighbours())
            {
                Console.WriteLine(n.ToString());
            }
        }

        private static void TestB()
        {
            //A <-> B <-> C
            Node a = new Node("a");
            Node b = new Node("b");
            Edge ab = a.ConnectTo(b);
            Node c = new Node("c");
            Edge bc = b.ConnectTo(c);
            Graph g = new Graph();
            g.Nodes.Add(a);
            g.Nodes.Add(b);
            g.Nodes.Add(c);
            g.Edges.Add(ab);
            g.Edges.Add(bc);
            Console.WriteLine(g.ToString());

            Console.WriteLine("Neighbours of B");
            foreach (Node n in b.GetNeighbours())
            {
                Console.WriteLine(n.ToString());
            }
        }

        private static void TestA()
        {
            //A <-> B
            Node a = new Node("a");
            Node b = new Node("b");
            Edge ab = a.ConnectTo(b);
            Graph g = new Graph();
            g.Nodes.Add(a);
            g.Nodes.Add(b);
            g.Edges.Add(ab);
            Console.WriteLine(g.ToString());
        }
    }

    class Edge
    {
        public Edge(string name, Node a, Node b)
        {
            Name = name;
            A = a;
            B = b;
        }

        public Node A { get; private set; }
        public Node B { get; private set; }
        public string Name { get; private set; }

        public override string ToString() => $"{Name}";
    }

    class Node
    {
        public Node(string name)
        {
            Name = name;
            connectedEdges = new List<Edge>();
        }

        public string Name { get; private set; }

        private ICollection<Edge> connectedEdges;
        public IEnumerable<Edge> ConnectedEdges
        {
            get
            {
                return connectedEdges.AsEnumerable();
            }
        }

        public void AddConnectedEdge(Edge e)
        {
            connectedEdges.Add(e);
        }

        public Edge ConnectTo(Node n)
        {
            //Create the edge with references to nodes
            Edge e = new Edge($"{Name} <-> {n.Name}", this, n);
            //Add edge reference to this node
            AddConnectedEdge(e);
            //Add edge reference to the other node
            n.AddConnectedEdge(e);
            return e;
        }

        public IEnumerable<Node> GetNeighbours()
        {
            foreach (Edge e in ConnectedEdges)
            {
                //Have to figure which one is not this node
                Node node = e.A != this ? e.A : e.B;
                yield return node;
            }
        }

        public override string ToString() => $"{Name}";
    }

    class Graph
    {
        public Graph()
        {
            Nodes = new List<Node>();
            Edges = new List<Edge>();
        }

        public ICollection<Node> Nodes { get; set; }
        public ICollection<Edge> Edges { get; set; }

        public override string ToString()
        {
            StringBuilder str = new StringBuilder();

            str.AppendLine("Graph:");
            str.AppendLine("Nodes:");
            foreach (Node n in Nodes)
            {
                str.AppendLine(n.ToString());
            }
            str.AppendLine("Edges:");
            foreach (Edge e in Edges)
            {
                str.AppendLine(e.ToString());
            }

            return str.ToString();
        }
    }
}

【讨论】:

  • 我一定会仔细看看这段代码 :) 事实上现在我只有从Edge 的角度来看的引用(它里面有一个开始和结束节点)。我想我这样做是因为很明显 - 你需要知道边缘在哪里开始和结束。
猜你喜欢
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-29
  • 1970-01-01
  • 1970-01-01
  • 2022-06-18
  • 1970-01-01
相关资源
最近更新 更多