C# · 12月 23, 2021

c# – 循环引用 – 架构问题

这可能是一个非常初学的问题,但我已经搜索了很多主题并且无法找到相同的情况,尽管我确信这种情况一直都在发生.

我的项目/计划将跟踪建筑项目图纸的变化,并在更改图纸时向人们发送通知.

将有许多建筑项目(工地),每个建筑项目中都会有很多图纸.每个图纸都会有一些修改(当它们被更改时,会创建一个新版本).

这是我的项目类

public class Project{ private readonly List<Drawing> _drawings = new List<Drawing>(30); private readonly List<Person> _autoRecepients = new List<Person>(30); public int ID { get; private set; } public string ProjectNumber { get; private set; } public string Name { get; private set; } public bool Archived { get; private set; } public List<Person> AutoRecepients { get { return _autoRecepients; } } public Project(int id,string projectNumber,string name) { if (id < 1) { id = -1; } ID = id; ProjectNumber = projectNumber; Name = name; } public bool AddDrawing(Drawing drawing) { if (drawing == null) return false; if (_drawings.Contains(drawing)) { return true; } _drawings.Add(drawing); return _drawings.Contains(drawing); } public void Archive() { Archived = true; } public bool DeleteDrawing(Drawing drawing) { return _drawings.Remove(drawing); } public IEnumerable<Drawing> ListDrawings() { return _drawings.AsReadOnly(); } public override string ToString() { return string.Format(“{0} {1}”,ProjectNumber,Name); }}

这是我的绘画课

public class Drawing : IDrawing{ private List<IRevision> _revisions = new List<IRevision>(5); private List<IssueRecord> _issueRecords = new List<IssueRecord>(30); private IRevision _currentRevision; public int ID { get; private set; } public string Name { get; private set; } public string Description { get; set; } public Project Project { get; private set; } public IRevision CurrentRevision { get { return _currentRevision; } } public Drawing(int id,string name,string description,Project project) { // To be implemented } /// <summary> /// Automatically issue the current revision to all Auto Recepients /// </summary> public void AutoIssue(DateTime date) { AutoIssue(date,_currentRevision); } /// <summary> /// Automatically issue a particular revision to all Auto Recepients /// </summary> public void AutoIssue(DateTime date,IRevision revision) { } public void IssueTo(Person person,DateTime date,IRevision revision) { _issueRecords.Add(new IssueRecord(date,this,revision,person)); throw new NotImplementedException(); } public void IssueTo(Person person,DateTime date) { IssueTo(person,date,_currentRevision); } public void IssueTo(IEnumerable<Person> people,DateTime date) { IssueTo(people,_currentRevision); } public void IssueTo(IEnumerable<Person> people,IRevision revision) { foreach (var person in people) { IssueTo(person,revision); } } public void Rename(string name) { if (string.IsNullOrWhiteSpace(name)) { return; } Name = name; } public void Revise(IRevision revision) { if (revision.Name == null ) return; _revisions.Add(revision); _currentRevision = revision; } public struct IssueRecord { public int ID { get; private set; } public DateTime Date { get; private set; } public IDrawing Drawing { get; private set; } public IRevision Revision { get; private set; } public Person Person { get; private set; } public IssueRecord(int id,IDrawing drawing,IRevision revision,Person person) { if (id < 1) { id = -1; } ID = id; Date = date; Drawing = drawing; Revision = revision; Person = person; } }}

这是Revision结构

public struct Revision : IRevision{ public int ID { get; private set; } public string Name { get; } public DateTime Date { get; set; } public IDrawing Drawing { get; } public IDrawingFile DrawingFile { get; private set; } public Revision(int id,IDrawingFile drawingFile) { if (name == null) { throw new ArgumentNullException(“name”,”Cannot create a revision with a null name”); } if (drawing == null) { throw new ArgumentNullException(“drawing”,”Cannot create a revision with a null drawing”); } if (id < 1) { id = -1; } ID = id; Name = name; Drawing = drawing; Date = date; DrawingFile = drawingFile; } public Revision(string name,IDrawingFile drawingFile) : this(-1,name,drawing,drawingFile) { } public Revision(string name,IDrawing drawing) : this(-1,DateTime.Today,null) { } public void ChangeID(int id) { if (id < 1) { id = -1; } ID = id; } public void SetDrawingFile(IDrawingFile drawingFile) { DrawingFile = drawingFile; }}

我的问题是在绘图类中的项目引用和修订结构中的绘图引用.
好像有点代码味道?
它似乎也可能导致将来序列化问题.
有一个更好的方法吗?

绘图对象似乎有必要知道它属于哪个项目,这样如果我正在使用单个绘图对象,我就可以知道它们属于哪个项目.

类似地,每个修订版本基本上由图纸“拥有”或者是图纸的一部分.没有绘图,修订没有意义,所以它需要引用它所属的绘图?

任何建议将不胜感激.

解决方法 你所拥有的不仅仅是循环引用,而是两个例子

可以从两端导航的亲子关系.

是的,这是正常的,可接受的,不是代码味道.是的,一些序列化工具需要您提示.例如Newtonsoft.Json需要ReferenceLoopHandling.Ignore设置.

作为一个概念的导航性并不总是在OO设计中被讨论,这是不幸的,因为它只是你想要的概念. (这是UML中的明确术语).

您通常不需要从两端进行导航.父子关系通常仅从父对子编码.这很常见.例如,invoiceline类很少需要其父发票的显式字段,因为大多数应用程序仅在检索父发票后查看该行.

所以设计决定不是,

“没有绘画,修改是否有意义?”

“我是否需要找到仅给出修订版的图纸?”

我的猜测是你的修订就像发票行,而不需要导航到他们的父母.图纸的答案< – >项目对我来说并不明显. (这是关于您的域名的分析问题,而不是代码式问题).

这是OO代码与sql之间的显着差异.在sql数据库中,它必须是包含对其父图形ID的引用的修订表.在OO代码中,父类几乎总是拥有对子代的引用.孩子们通常不需要引用他们的父母,因为你访问孩子的唯一方法是已经拥有父母.