Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / threads

Strongly Typed Sub-Lists Which Modify Their Parent List (No Events)

5.00/5 (1 vote)
5 Oct 2012CPOL 11.8K  
Generic list wrapper that returns a smaller strongly typed sub-list which modifies the parent-list when changed, without using events

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:

C#
/// <summary>
/// A class which wraps a parent list containing mixed object types, to
/// provide smaller strongly typed sub-lists of objects.
/// These sub-lists can be modified, which then modifies the parent list
/// concurrently (without events being triggered).
/// <para>Class is serializable</para>.
/// </summary>
/// <typeparam name="L">Type of objects in the sub list</typeparam>
/// <typeparam name="P">The type objects in the parent list
/// (from which <typeparamref name="L"/> derives or implements) </typeparam>
[Serializable]
public sealed class SubList<L, P> : IList<L>
{
    private List<P> _parentList;
    private List<L> _tempList; //memory holder (for enumeration)
 
    /// <summary>
    /// For deserializer only. Do not use this constructor.
    /// </summary>
    internal protected SubList() { }
 
    /// <summary>
    /// List Constructor
    /// </summary>
    /// <param name="parentList">Reference to a parent list which contains mixed objects 
    /// derived from <typeparamref name="P"/></param>        
    public SubList(ref List<P> parentList)
    {
        //construct the reference if a null value is passed
        if (parentList == null)
            parentList = new List<P>();
        this._parentList = parentList;
    }
 
    /// <summary>
    /// Create a shallow copy of this sublist to a list of type <typeparamref name="L"/>
    /// </summary>
    /// <returns>A strongly typed list copy of the objects of type 
    /// <typeparamref name="L"/></returns>
    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:

C#
[Serializable, DesignerCategory("code")]
public abstract class Base
{   
    /// <summary>
    /// for deserializer only
    /// </summary>
    internal Base() { }
}
 
[Serializable, DesignerCategory("code")]
[XmlType("IndexItemAdd")]
public class Add : Base
{    
    /// <summary>
    /// for deserializer only
    /// </summary>
    internal Add() : base() { }
}
 
[Serializable, DesignerCategory("code")]
[XmlType("IndexItemDelete")]
public class Delete : Base
{
    /// <summary>
    /// for deserializer only
    /// </summary>
    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; }
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)