One way to achieve this goal is to implement an ObservableCollection for your Foo class to listen for changes inside the list, and after adding / removing items, you can add a parent to the child of the Bar.
Thus, your method should not refer to the parent, but it can access it through the Property Parent , which will be of type Foo .
Since they all refer to the same Foo instance inside the Foo instance, you will not have duplicate data.
As an example of such an implementation, you can do this as follows:
First, define an interface that provides easy access to the Parent property.
public interface IChild<T> { T Parent { get; } }
And we could do the same for the DoSomething Bar method
public interface IBar { void DoSomething(); }
And we implement two interfaces inside the abstract version of Bar, leaving the abstract DoSomething methods
public abstract class Bar : IChild<Foo>, IBar { private Foo parent; public Foo Parent { get { return parent; } set { parent = value; } } public abstract void DoSomething(); }
and implementations of two versions of Bar, for example, for example:
public class Bar1 : Bar { public override void DoSomething() { if (this.Parent == null) { throw new ArgumentException("Parent cannot be null"); }
Then you need to make changes to your Foo class, which registers in the collection (Bar1Collection, Bar2Collection) and offers a way to listen for changes in the collection. It also implements the IDisposable interface so that we can unregister from CollectionChanged events when we no longer need the Foo class
public class Foo : IDisposable { private readonly IList<Bar> bar1Collection = new ObservableCollection<Bar>(); public IList<Bar> Bar1Collection { get { return bar1Collection; } } private readonly IList<Bar> bar2Collection = new ObservableCollection<Bar>(); public IList<Bar> Bar2Collection { get { return bar2Collection; } } protected void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (var item in e.OldItems) { if (item is Bar) { var bar = item as Bar; bar.Parent = null; } } } if (e.NewItems != null) { foreach (var item in e.NewItems) { if (item is Bar) { var bar = item as Bar; bar.Parent = this; } } } } protected void RegisterCollection(INotifyCollectionChanged collection) { if (collection == null) { return; } collection.CollectionChanged += OnCollectionChanged; } protected void UnregisterCollection(INotifyCollectionChanged collection) { if (collection == null) { return; } collection.CollectionChanged -= OnCollectionChanged; } public Foo() { RegisterCollection(Bar1Collection as INotifyCollectionChanged); RegisterCollection(Bar2Collection as INotifyCollectionChanged); } private bool isDisposed = false; protected virtual void Dispose(bool disposing) { if (!disposing || isDisposed) { return; } isDisposed = true; UnregisterCollection(Bar1Collection as INotifyCollectionChanged); UnregisterCollection(Bar2Collection as INotifyCollectionChanged); } public void Dispose() { Dispose(true); } }
As a test method, this console program could work against it
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; namespace BarFoo { class Program { static void Main(string[] args) { Foo foo1 = new Foo(); Bar bar1 = new Bar1(); Bar bar2 = new Bar2(); foo1.Bar1Collection.Add(bar1); foo1.Bar2Collection.Add(bar2); Debug.Assert(bar1.Parent != null); Debug.Assert(bar2.Parent != null); bar1.DoSomething(); bar2.DoSomething(); Console.WriteLine("Done"); Console.ReadLine(); foo1.Dispose(); } } }