この問題は、クラスの階層があり、子ノードが親ノードにアクセスできる必要がある場合によく発生します。
より多くの情報がなければ、何が最適かを判断するのは困難ですが、いくつかのオプションがあります。
- つまり、親ノードへの参照を保持するフィールドを作成し、これらの参照を手動で追跡します。
- 親ノードへの参照を保持するフィールドを用意し、これが自動的に実行されるようにクラスを配置します (このアプローチには欠点がありますが、以下で説明します)。
- 各子ノードの親へのマッピングを保持し、手動で更新する別のクラスを用意します (子クラスに追加のフィールドを持たないようにしたい場合)。
- 親コレクションから子ノードを追加または削除し、これらの参照を更新するために使用する必要がある別のクラスを用意します。
myVisualServers.Add
これには、またはを使用して直接追加するのではなく、すべての挿入と削除をこのクラスで行う必要がありますmyVisualServers.Remove
。
- これらのメソッドの呼び出し元に親クラスへの参照を渡すようにします (@tamijud が提案したように)。これは、正しい親を取得すること、または呼び出し元がこの情報をまったく知っていることを保証するものではありません。
- 最後に
myVisualServers
、クラスのインスタンス化後にコレクションが不変であると想定されている場合VisualService
、コンストラクター内でコレクション全体を渡し、そこにすべての参照を配線できるため、物事が単純化される可能性があります。
この割り当てを自動化したい場合はAbstractVisualServer
、アイテムの挿入と削除でこれを実行する独自の「スマート」コレクションを作成できます。ただし、このアプローチの問題は、この特定の実装に縛られており、他のインターフェイスに依存してこれを行うことができないことです (別の実装がIVisualService
同じことを保証するものはありません)。
その場合、これがあなたが達成したいことだと思います:
Parent parent = new Parent() { Name = "Parent" };
Child child = new Child() { Name = "Child" };
// starting conditions
Debug.Assert(parent.Children.Count == 0);
Debug.Assert(child.Parent == null);
// adding a child should set its parent
parent.Children.Add(child);
Debug.Assert(child.Parent == parent);
// removing a child should clear its parent
parent.Children.Remove(child);
Debug.Assert(child.Parent == null);
// setting a parent should add that child to the collection
child.Parent = parent;
Debug.Assert(parent.Children.Contains(child));
// clearing a parent should remove that child from the collection
child.Parent = null;
Debug.Assert(parent.Children.Count == 0);
Add
まず、インターセプトとRemove
メソッドを実行するカスタム コレクション クラスを作成する必要があります。
public class Parent
{
public string Name { get; set; }
// Note that this is a custom private collection class
private readonly ChildCollection _children;
public ICollection<Child> Children
{
get { return _children; }
}
private class ChildCollection :
System.Collections.ObjectModel.Collection<Child>
{
private readonly Parent _parent;
public ChildCollection(Parent parent)
{
_parent = parent;
}
protected override void InsertItem(int index, Child item)
{
// remove from previous parent
if (item.Parent != null)
item.Parent.Children.Remove(item);
base.InsertItem(index, item);
// assign the new parent
item.Parent = _parent;
}
protected override void RemoveItem(int index)
{
// this item no longer has a parent
var item = this[index];
base.RemoveItem(index);
item.Parent = null;
}
protected override void SetItem(int index, Child item)
{
// remove from previous parent
if (item.Parent != null)
item.Parent.Children.Remove(item);
base.SetItem(index, item);
// assign the new parent
item.Parent = _parent;
}
protected override void ClearItems()
{
foreach (var i in this)
i.Parent = null;
base.ClearItems();
}
}
public Parent()
{
_children = new ChildCollection(this);
}
}
また、Child
クラスは「親を認識する」必要があります。
public class Child
{
public string Name { get; set; }
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if (_parent == value)
return;
if (_parent != null && _parent.Children.Contains(this))
_parent.Children.Remove(this);
_parent = value;
if (_parent != null && !_parent.Children.Contains(this))
_parent.Children.Add(this);
}
}
}