[英]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.