Monitor your collection
There are times when you want your class to expose some kind of collection. Maybe you got a Server class and want to world to access it connections, or you got a Family class and want to provide access to the collection of family members. Sometimes that is easy - just to return a reference to the collection from a function or property. Other times it’s not that easy.
Let say that you want expose a collection, but you don’t want anybody to modify that collection. There are different ways to solve that. You could return a clone of your collection, or maybe convert it into an array.
But what if you want to allow users to modify your collection, but still keep an eye on the actions taken. Maybe you want to restrict specific persons from being added to a a family, or maybe you want to temporarily suspend a maintenance job, while someone is trying to modify your connection collection… the list could go on and on.
A typically solution is to create a specialized collection, but that is often more work than necessary. Another way its to creaet a more generic reusable collection ‘wrapper’ that is capable of thrown event when actions (add, remove, clear) are performed. Here is an example of such a collection:
namespace NotifyingCollection
{
using System.Collections.Generic;
public class NotifyingCollectionEventArgs
{
public T Item;
public bool Cancel; // only valid for OnBeforeXXX events
public NotifyingCollectionEventArgs(T item)
{
this.Item = item;
this.Cancel = false;
}
}
public class NotifyingCollection : ICollection
{
public delegate void NotifyingCollectionDelegate(
NotifyingCollection source,
NotifyingCollectionEventArgs ev);
public event NotifyingCollectionDelegate OnBeforeAdd;
public event NotifyingCollectionDelegate OnAfterAdd;
public event NotifyingCollectionDelegate OnBeforeRemove;
public event NotifyingCollectionDelegate OnAfterRemove;
public event NotifyingCollectionDelegate OnBeforeClear;
public event NotifyingCollectionDelegate OnAfterClear;
private ICollection m_pInnerCollection;
public NotifyingCollection(ICollection pInner)
{
m_pInnerCollection = pInner;
}
public void Add(T item)
{
NotifyingCollectionEventArgs ev =
new NotifyingCollectionEventArgs (item);
// Notify listener that we’re about to add an item
if (OnBeforeAdd != null)
OnBeforeAdd(this, ev);
// Was the action canceled?
if (ev.Cancel)
return;
// Add item
m_pInnerCollection.Add(ev.Item);
// Notify listened that we’ve added the item
if (OnAfterAdd != null)
OnAfterAdd(this, ev);
}
public void Clear()
{
NotifyingCollectionEventArgs ev =
new NotifyingCollectionEventArgs (default(T));
// Notify listener that we’re about to clear the collection
if (OnBeforeClear != null)
OnBeforeClear(this, ev);
// Was the action canceled?
if (ev.Cancel)
return;
// Clear the list
m_pInnerCollection.Clear();
// Notify listened that we’ve cleared the collection
if (OnAfterClear != null)
OnAfterClear(this, ev);
}
public bool Remove(T item)
{
NotifyingCollectionEventArgs ev =
new NotifyingCollectionEventArgs (item);
// Notify listener that we’re about to remove an item
if (OnBeforeRemove != null)
OnBeforeRemove(this, ev);
// Was the action canceled?
if (ev.Cancel)
return false;
// Remove the item
bool bRet = m_pInnerCollection.Remove(ev.Item);
// Notify listener that we’ve removed an item
if (OnAfterRemove != null)
OnAfterRemove(this, ev);
return bRet;
}
#region Thin Wrappers
public bool Contains(T item)
{
return m_pInnerCollection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
m_pInnerCollection.CopyTo(array, arrayIndex);
}
public int Count
{
get
{
return m_pInnerCollection.Count;
}
}
public bool IsReadOnly
{
get
{
return m_pInnerCollection.IsReadOnly;
}
}
public IEnumerator GetEnumerator()
{
return m_pInnerCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return m_pInnerCollection.GetEnumerator();
}
#endregion
}
}
And here’s a small example utilizing our generic event throwing collection. The samples consist of an Test class that exposes a collection of integers through a public property. The OnBeforeAdd event is handled to cancel any attempt to add 24 the list (24 is just for demonstration purposes… you could do whatever you like):
namespace NotifyingCollectionTest
{
using System.Collections.Generic;
using NotifyingCollection;
public class Test
{
private List intList;
private NotifyingCollection notifyingIntList;
public Test()
{
intList = new List ();
notifyingIntList = new NotifyingCollection (intList);
notifyingIntList.OnBeforeAdd += notifyingIntList_OnBeforeAdd;
}
public ICollection IntList
{
get
{
return notifyingIntList;
}
}
void notifyingIntList_OnBeforeAdd(NotifyingCollection source,
NotifyingCollectionEventArgs ev)
{
// don’t allow any ‘24′ into our list
if (ev.Item == 24)
ev.Cancel = true;
}
}
class Program
{
static void Main(string[] args)
{
Test pTest = new Test();
pTest.IntList.Add(23); // add 23 to list
pTest.IntList.Add(24); // add 24 to list
pTest.IntList.Add(25); // add 25 to list
return;
}
}
}












