[英]Implementation of an enforced one-to-many relationship in C#?
在我的应用中,我正在使用Simfile和Notechart对象。 Simfile本质上是一个Notechart容器,具有以下约束:
1)任何时候,每个记事本都必须完全包含在一个父Simfile中。
2)给定一个Simfile对象,我需要能够获取其所有包含的Notecharts(应该很容易)。
3)给定一个记事本,我需要能够获取其父Simfile(更难)。
我当前的实现方式是使用Dictionary作为Simfile的基类,因为每个Notechart至少在Simfile中具有唯一的键。 我面临的问题是如何强制Notechart始终具有父Simfile? 现在,我可以独立于Simfile创建一个Notechart,并将其添加到Simfile字典中,而无需从Notechart-> Simfile中进行所需的引用。
理想情况下,最佳分辨率是Simfile中的Add()方法,该方法只能从Notechart的构造函数中调用,但我认为这是不可能的。
我想说这是一个普遍的问题,但这是我在实践中第一次遇到它,我不确定它叫什么。
实现此目的的一种方法是不允许外部调用者创建NoteChart实例,并在Simfile上具有一个方法,该方法创建并返回已经连接到Simfile实例的NoteChart实例。
这是一个简单的示例,请注意,我没有为重复的ID等添加任何错误检查。
public class NoteChart
{
public SimFile Owner {get; private set;}
public string Id { get; private set; }
internal NoteChart(SimFile owner, string id)
{
this.Owner = owner;
this.Id = id;
Owner.Add(id, this);
}
}
public class SimFile : Dictionary<string, NoteChart>
{
public NoteChart CreateNoteChart(string id)
{
NoteChart noteChart = new NoteChart(this, id);
return noteChart;
}
}
然后,将使用以下内容
// Get instance of SimFile somehow. I just newed it here for the example
SimFile sim = new SimFile();
// Create a new NoteChart on the SimFile
NoteChart chart = sim.CreateNoteChart("NoteChart-1");
这里的事情是NoteChart构造函数被标记为内部。 这意味着其他程序集中的其他类将无法直接构造NoteChart
类的实例,并将被迫通过SimFile.CreateNoteChart
方法进行工作。
在同一个程序集中,代码仍然可以调用internal
构造函数,但是必须传递拥有的SiteFile和Id,构造函数会将NoteChart添加到SimFile。
您可能需要重新考虑从Dictionary继承SimFile的设计选择,最好仅将Dictionary作为成员并公开所需的功能。 例如,现在可能会通过SimFle.Add将NoteChart无意中直接添加到另一个SimFile实例,该实例是从Dictionary继承的,并且ID不匹配。 但这只是一些思考。
您可以使用类似于以下的代码结构:
public class Simfile
{
public Simfile()
{
Notecharts = new List<Notechart>();
}
public List<Notechart> Notecharts { get; private set; }
}
public class Notechart
{
public Notechart(Simfile simfile)
{
Simfile = simfile;
simfile.Notecharts.Add(this);
}
public Simfile Simfile { get; private set; }
}
您唯一需要确保的是添加到Notechart类的所有其他构造函数都必须带有Simfile参数。
编辑(基于评论)
如果您不想确保不能将Notechart添加到Simfile中(如果不是它,它是父simfile),则可以执行以下更改:
public class Simfile
{
public Simfile()
{
ChildNotecharts = new List<Notechart>();
}
private List<Notechart> ChildNotecharts { get; set; }
public IEnumerable<Notechart> Notecharts
{
get { return ChildNotecharts; }
}
public int AddNotechart(Notechart notechart)
{
if (ChildNotecharts.Contains(notechart)) return 0;
if (notechart.Simfile != this) return -1;
ChildNotecharts.Add(notechart);
return 1;
}
}
public class Notechart
{
public Notechart(Simfile simfile)
{
Simfile = simfile;
simfile.AddNotechart(this);
}
public Simfile Simfile { get; private set; }
}
这里有很多解决方案。
在NoteChart中有一个接受Simfile对象的构造函数,并在Simfile对象中调用一个添加NoteChart的函数。 如果具有这样的参数化构造函数,则默认构造函数不再可用。 像这样:
public class SimFile
{
public void AddNoteChart(NoteChart nc)
{
// Here you can add the note chart to the SimFile dictionary
}
}
public class NoteChart
{
public SimFile PapaSimFile { get; private set; }
NoteChart(SimFile simFile)
{
if (simFile == null)
{
// throw exception here
}
PapaSimFile = simFile;
PapaSimFile.AddNoteChart(this);
}
}
另一个选择是使NoteChart仅可从SimFile内部访问。 例如:
public class SimFile
{
public class NoteChart
{
public SimFile SimFile { get; internal set; }
}
public NoteChart CreateNoteChart()
{
NoteChart nc = new NoteChart();
nc.SimFile = this;
// Here you can add the note chart to the SimFile dictionary
return nc;
}
}
每种方法都有优点和缺点,您应该从整体上看以确定哪个更好。
您的主要要求似乎是您不希望外部代码修改SimFile
除非创建新的NoteCharts
。 因此,我们绝对需要:
限制对NoteChart
构造函数的访问(否则,您可能会有一个NoteChart
实例,其父SimFile
实际上并不包含该实例)
限制对SimFile
字典的访问(否则其他代码可能会添加一些东西)
允许访问SimFile
词典的只读功能。
这就是我要做的。
public class NoteChart
{
public SimFile Parent { get; private set; }
public string Id { get; private set; }
// internal: code outside this assembly cannot instantiate this class
internal NoteChart(SimFile parent, string id)
{
this.Parent = parent;
this.Id = id;
Parent.dictionary.Add(id, this);
}
}
// Implement only IEnumerable, as that is read-only;
// all the other collection interfaces are writable
public class SimFile : IEnumerable<KeyValuePair<string, NoteChart>>
{
// internal: not accessible to code outside this assembly
internal Dictionary<string, NoteChart> dictionary =
new Dictionary<string, NoteChart>();
// Public method to enable the creation of a new note chart
// that is automatically associated with this SimFile
public NoteChart CreateNoteChart(string id)
{
NoteChart noteChart = new NoteChart(this, id);
return noteChart;
}
// Read-only methods to retrieve the data. No writable methods allowed.
public NoteChart this[string index] { get { return dictionary[index]; } }
public IEnumerator<KeyValuePair<string, NoteChart>> GetEnumerator() {
return dictionary.GetEnumerator(); }
public int Count { get { return dictionary.Count; } }
public IEnumerable<string> Keys { get { return dictionary.Keys; } }
public IEnumerable<NoteChart> Values { get { return dictionary.Values; } }
}
以下实现了一个始终具有父SimFile的Notechart。 它的工作方式是隐藏容器(以防止在不更改父文件的情况下将其添加到SimFile中),并使Notechart成为SimFile类的一部分,以便在父文件更改时可以访问私有集合。 这很自然,因为您的要求是没有相应的SimFile便不存在Notecharts。 它们确实是SimFiles的一部分。
public class SimFile
{
private List<Notechart> _notecharts = new List<Notechart>();
public IEnumerable<Notechart> Notecharts
{
get {return _notecharts.AsEnumerable<Notechart>();}
}
public class Notechart
{
public Notechart(SimFile parent)
{
this.Parent = parent;
}
private SimFile _parent = null;
public SimFile Parent
{
get {return _parent;}
set
{
if (value != null)
{
if (_parent != null)
{
_parent._notecharts.Remove(this);
}
_parent = value;
_parent._notecharts.Add(this);
}
else
{
throw new Exception("A Notechart MUST have a parent SimFile");
}
}
}
}
}
好吧,我知道了。 我将Notechart类抽象化了(已完全实现,但是抽象化以防止其他类实例化它),然后在Simfile中使用受保护的构造函数在Simfile中派生了一个内部类(NotechartWorking)。 然后,我遵循了此处的建议,以使Simfile为我生成Notecharts。 因此,我正在创建NotechartWorking对象,但是将它们作为Notecharts返回,最终效果很好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.