Click here to Skip to main content
Click here to Skip to main content

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

, 5 Oct 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

/// <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:

[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)

Share

About the Author

Bryan Lyman
Software Developer (Senior) Centeva
United States United States
I achieved my degree in Electronics Engineering, however, my true passion has always been programming. I started programming at a very young age using Basic on a TRS-80 and saving my programs on audio tape through an audible modem. I moved up to Basic on an Atari 800XL computer, saving my work on 5.25 Floppy Disks. I then learned Basic on an Apple IIe, saving my work on 3.5 floppies. When I approached Highschool I began getting into lower level languages such as Borland Pascal on IBM 8086 machines using DOS. Gaining a love of early video games (gotta love Ultima 3 through 7), I endeavored to write my own games and DOS utilities using Borland C++ and Intel x86 Assembly language. I began a career in software engineering during college using everything from Rex on OS/2 to .Net Studio v1.0 (some tech support jobs thrown in here and there). I am now a big proponent for C#, I believe that (standards-wise) it is where C++ should have been many years ago. Today I write everything from Native apps for PC, Mac and smart-phones; to Web applications. Trends change quickly, but I perceive the most useful form of programming currently is Web Applications, Cloud services, asynchronous Ajax, and JQuery JavaScript libraries.
Follow on   LinkedIn

Comments and Discussions

 
SuggestionSimplifying... PinmemberAndrew Rissing10-Oct-12 6:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 5 Oct 2012
Article Copyright 2012 by Bryan Lyman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid