有什么方法可以访问 'super' class 的字段(不是继承的)
Is there any way to access fields of 'super' class (not inherited)
标题可能会产生误导,因为我在搜索甚至创建一个正确的问题时遇到了一些问题,所以让我给出一个我正在努力解决的真实问题:
我有一个Graph
class。由于图形需要节点和边,我创建了两个额外的 classes Node
(顶点)和 Edge
。我的结构如下所示:
class Graph
{
List<Node> nodes;
List<Edge> edges;
public Graph( ... ) { /* populate lists */ }
}
class Node { ... }
class Edge { ... }
我为Node
class写了一些方法,有一个对我来说特别有问题。签名:
public List<Node> GetNeighbours(List<Edge> edges) { ... }
相当标准。给定一张图,我问一个节点:你有多少个邻居?我需要边缘列表来解决它。
如何重构这段代码,以便我可以在内部使用 Graph
properties/fields 而不是每次都传递边列表?这样的事情可能吗:
public List<Node> GetNeighbours()
{
// ...
foreach(edge in *BASE*.edges) { ... }
}
我知道我不能使用 base
关键字,因为我不想在这里进行任何继承(为什么节点必须从图继承?!)和嵌套 classes似乎对我也没有帮助(无法访问 "parent's" 字段)。
这段代码现在可以运行,但我觉得它不够优雅,我想体验一下合适的解决方案。
在 Graph
构造函数中传递对父 class 的引用。
类似于:
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) { ... }
}
根据对您要执行的操作的描述:
Given a graph I ask a node: how many neighbours do you have?
您确定这应该是 Node
的方法吗?由于 Graph
包含 Nodes
和 Edges
也许这种方法在 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
的引用。
我会在 Graph
上使用类似 Graph.AddNodes()
或 Graph.AddEdges()
的方法,因此这是确保所有 Nodes
(and/or Edges
) 有它需要的引用。我在想这样的事情,这取决于 Node
和 Edge
的型号。
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; }
}
这是另一种方法。您可以让每条边知道每一端的节点,而不是传递父引用。并让每个节点都知道连接到它们的边。
这样做的一个巨大优势是您不需要列举可能大量的 nodes/edges 来找到您需要的东西。您已经拥有您需要的东西,所以速度要快得多。
这是我描述的方法的快速示例以及一些测试:
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();
}
}
}
标题可能会产生误导,因为我在搜索甚至创建一个正确的问题时遇到了一些问题,所以让我给出一个我正在努力解决的真实问题:
我有一个Graph
class。由于图形需要节点和边,我创建了两个额外的 classes Node
(顶点)和 Edge
。我的结构如下所示:
class Graph
{
List<Node> nodes;
List<Edge> edges;
public Graph( ... ) { /* populate lists */ }
}
class Node { ... }
class Edge { ... }
我为Node
class写了一些方法,有一个对我来说特别有问题。签名:
public List<Node> GetNeighbours(List<Edge> edges) { ... }
相当标准。给定一张图,我问一个节点:你有多少个邻居?我需要边缘列表来解决它。
如何重构这段代码,以便我可以在内部使用 Graph
properties/fields 而不是每次都传递边列表?这样的事情可能吗:
public List<Node> GetNeighbours()
{
// ...
foreach(edge in *BASE*.edges) { ... }
}
我知道我不能使用 base
关键字,因为我不想在这里进行任何继承(为什么节点必须从图继承?!)和嵌套 classes似乎对我也没有帮助(无法访问 "parent's" 字段)。
这段代码现在可以运行,但我觉得它不够优雅,我想体验一下合适的解决方案。
在 Graph
构造函数中传递对父 class 的引用。
类似于:
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) { ... }
}
根据对您要执行的操作的描述:
Given a graph I ask a node: how many neighbours do you have?
您确定这应该是 Node
的方法吗?由于 Graph
包含 Nodes
和 Edges
也许这种方法在 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
的引用。
我会在 Graph
上使用类似 Graph.AddNodes()
或 Graph.AddEdges()
的方法,因此这是确保所有 Nodes
(and/or Edges
) 有它需要的引用。我在想这样的事情,这取决于 Node
和 Edge
的型号。
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; }
}
这是另一种方法。您可以让每条边知道每一端的节点,而不是传递父引用。并让每个节点都知道连接到它们的边。
这样做的一个巨大优势是您不需要列举可能大量的 nodes/edges 来找到您需要的东西。您已经拥有您需要的东西,所以速度要快得多。
这是我描述的方法的快速示例以及一些测试:
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();
}
}
}