让我在开始之前先声明我看到了与此类似的帖子,但是没有一个解决方案令我满意和/或应用于C#。
我有一个Graph
类,它由Node
和Connection
个对象组成。该图包含由与其关联的所有子Node和Connection对象组成的集合。除此之外,每个Node都有一个Connection对象的集合。
请注意:这是一个简化的玩具问题。您可以查看实际的(进行中的)生产代码here。在生产中,Neuron
是Node
,而Axon
是Connection
。
public class Graph : IDeepCloneable<Graph>
{
// These are basically just Dictionary<int,object>s wrapped in an ICollection
public NodeCollection Nodes;
public ConnectionCollection Connections;
public Graph Clone()
{
return new Graph
{
Nodes = this.Nodes.Clone(),
Connections = this.Connections.Clone()
};
}
}
public class Node : IDeepCloneable<Node>
{
public int Id;
public NodeConnectionCollection Connections;
// NodeConnectionCollection is more or less the same as NodeCollection
// except that it stores Connection objects into '.Incoming' and '.Outgoing' properties
public Node Clone()
{
return new Node
{
Id = this.Id,
Connections = this.Connections.Clone()
};
}
}
public class Connection : IDeepCloneable<Connection>
{
public int Id;
public Node From;
public Node To;
public Connection Clone()
{
return new Connection
{
Id = this.Id,
From = this.From.Clone(),
To = this.To.Clone()
};
}
}
public class ConnectionCollection : ICollection<Connection>, IDeepCloneable<ConnectionCollection>
{
private Dictionary<int, Connection> idLookup;
private Dictionary<ProjectionKey, Connection> projectionLookup;
public int Count => idLookup.Count;
public bool IsReadOnly => false;
public Add( Connection conn )
{
idLookup.Add( conn.Id, conn );
projectionLookup.Add( new ProjectionKey( conn.From, conn.To ), conn );
}
...
internal struct ProjectionKey
{
readonly intFrom;
readonly int To;
readonly int HashCode;
public ProjectionKey( int from, int to )
{
From = from;
To = to;
HashCode = ( 23 * 397 + from ) * 397 + to;
}
public override int GetHashCode() { return HashCode; }
}
}
public class NodeCollection : ICollection<Node>, IDeepCloneable<NodeCollection>
{
private Dictionary<int, Node> nodes;
private Dictionary<int, InputNode> inputNodes;
private Dictionary<int, InnerNode> innerNodes;
private Dictionary<int, OutputNode> outputNodes;
...
public Node this[ int id ]
{
get => nodes[ id ];
}
}
每个对象都支持深度克隆,其主要思想是使用类可以在子类上调用Clone()
并以此方式处理堆栈。
但是,这在生产中不可行。对Graph.Clone()
的调用将克隆NodeCollection
和ConnectionCollection
字段,这将克隆存储在其中的每个Node
和Connection
实例,而每个实例都将克隆其他引用子元素。
一个常见的解决方案似乎是存储每个子对象的ID,然后在完成所有克隆后重建引用。但是,据我所知,这需要引用父对象并紧密耦合数据结构。
我对如何正确处理这个问题感到非常困惑。 我需要合理的性能,因为我的应用程序(遗传算法)会不断执行克隆操作,但是在这种情况下,我对寻找一种鲁棒的设计模式或实现方式更感兴趣。进行此图结构的深层克隆,同时在后台隐藏大量繁琐的工作。
是否有任何设计模式可以让我按原样克隆此数据结构,同时更新循环引用并保持其完整性?
答案 0 :(得分:1)
我的建议是将解决问题的方法从克隆更改为重新创建。我已经解决了一个类似的问题,即保存从用户界面手动创建的图形用户,然后在导入保存的图形时重新创建它。考虑一下,听起来几乎是一样的。
所以我想出的解决方案是从中央控制序列化图形(考虑到您正在使用启发式修改图形,我假设您对图形具有中央控制)。即使您没有图形的中央控制,我相信也可以通过某种方式遍历它来获取所有信息。
最简单的图形是邻居信息的集合。
也可以是有向的或无向的
1 -> 2
1 -> 3
3 -> 2
因此,如果您想出一种生成此类列表的方法,则只需调整此简单列表即可创建新图形。
或者另一种方法是列出您的节点及其邻居,如下所示,
1, [2,3]
3, [2]
在我看来,这甚至更容易重新创建图形。
如果您对此感到好奇,这里是我运用这种方法的项目中的file-我认为这并不是答案或问题的参考。