Introduction
Let's say there are two classes named "Add
" and "Delete
", derived from a class named "Base
". We want a list of "Base
" objects, but this master list can contain a mixture of any objects derived from "Base
" as well. I want to be able to retrieve a strongly typed sub-list of only "Add
" objects from the Master list, but if I insert or remove "Add
" objects from the sub-list, I want the master list to be modified as well without extra code, and without the slow-down and thread unsafety of events being thrown constantly.
Here is the solution:
[Serializable]
public sealed class SubList<L, P> : IList<L>
{
private List<P> _parentList;
private List<L> _tempList;
internal protected SubList() { }
public SubList(ref List<P> parentList)
{
if (parentList == null)
parentList = new List<P>();
this._parentList = parentList;
}
public List<L> ShallowCopy()
{
List<L> ret = new List<L>();
this._parentList.ForEach(delegate(P item)
{
if (item is L)
ret.Add((L)(object)item);
});
return ret;
}
public L[] ToArray()
{
List<L> ret = this.ShallowCopy();
return ret.ToArray();
}
public bool IsParentValid()
{
return (this._parentList != null);
}
public bool IsValid(L item)
{
return (this.IsParentValid() && item is P);
}
public void Merge(List<L> list)
{
list.ForEach(delegate(L item)
{
if (IsValid(item) && !this._parentList.Contains((P)(object)item))
this._parentList.Add((P)(object)item);
});
}
#region IList<L> Members
public int IndexOf(L item)
{
if (IsValid(item))
return this._parentList.IndexOf((P)(object)item);
return -1;
}
public void Insert(int index, L item)
{
if (IsValid(item))
this._parentList.Insert(index, (P)(object)item);
}
public void RemoveAt(int index)
{
this._parentList.RemoveAt(index);
}
public L this[int index]
{
get
{
P ret = this._parentList[index];
if (ret is L)
return (L)(object)ret;
return default(L);
}
set
{
P set = default(P);
if (index < this._parentList.Count)
set = this._parentList[index];
if (set is L || set != null)
this._parentList[index] = (P)(object)value;
}
}
#endregion
#region ICollection<L> Members
public void Add(L item)
{
if (IsValid(item))
this._parentList.Add((P)(object)item);
}
public void Clear()
{
foreach (P item in this._parentList)
if (item is L)
this._parentList.Remove((P)(object)item);
}
public bool Contains(L item)
{
if (IsValid(item))
return this._parentList.Contains((P)(object)item);
return false;
}
public void CopyTo(L[] array, int arrayIndex)
{
List<L> ret = this.ShallowCopy();
if (ret.Count > 0)
ret.CopyTo(array, arrayIndex);
}
public int Count
{
get
{
int ret = 0;
foreach (P item in this._parentList)
if (item is L)
ret++;
return ret;
}
}
bool ICollection<L>.IsReadOnly
{
get { return ((ICollection<P>)this._parentList).IsReadOnly; }
}
public bool Remove(L item)
{
if (IsValid(item))
return this._parentList.Remove((P)(object)item);
return false;
}
#endregion
#region IEnumerable<L> Members
IEnumerator<L> IEnumerable<L>.GetEnumerator()
{
this._tempList = this.ShallowCopy();
return this._tempList.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this._parentList.GetEnumerator();
}
#endregion
}
Here is an example of how to implement the class using an XML serializable format:
[Serializable, DesignerCategory("code")]
public abstract class Base
{
internal Base() { }
}
[Serializable, DesignerCategory("code")]
[XmlType("IndexItemAdd")]
public class Add : Base
{
internal Add() : base() { }
}
[Serializable, DesignerCategory("code")]
[XmlType("IndexItemDelete")]
public class Delete : Base
{
internal Delete() : base() { }
}
[Serializable()]
[DesignerCategory("code")]
[XmlType("IndexItem")]
public class IndexItem
{
private List<Base> allItems;
private SubList<Add, Base> indexItemAddFields;
private SubList<Delete, Base> indexItemDeleteFields;
[XmlElement("IndexItemAdd")]
public SubList<Add, Base> Adds
{
get { return this.indexItemAddFields; }
set
{
if (value != null && value.IsParentValid())
this.indexItemAddFields = value;
else
this.indexItemAddFields = new SubList<Add, Base>(ref allItems);
}
}
[XmlElement("IndexItemDelete")]
public SubList<Delete, Base> Deletes
{
get { return this.indexItemDeleteFields; }
set
{
if (value != null && value.IsParentValid())
this.indexItemDeleteFields = value;
else
this.indexItemDeleteFields = new SubList<Delete, Base>(ref allItems);
}
}
[XmlIgnore]
public List<Base> Items
{
get { return this.allItems; }
}
}