Click here to Skip to main content
15,867,594 members
Articles / Programming Languages / C#

Generic List (C#)

Rate me:
Please Sign up or sign in to vote.
4.72/5 (23 votes)
12 Aug 2009CPOL1 min read 182.5K   2.9K   51   11
Generic List based object that acts as a List but with built-in events

Introduction

This is a C# List based object that has the same functionality as a list but as the built-in events that should have been there from the start. It has the following events:

  • BeforeItemAdded
  • ItemAdded
  • BeforeItemRemoved
  • ItemRemoved
  • ItemsCleared

Background 

I created this GenericList because I kept finding myself needing events like ItemAdded or ItemRemoved everytime I used the list. So I created this generic list. 

The GenericList Inherits from the List Interface 

You can see that implemented from the IList interface. I took all the functions that the interface implements and wrapped it back to a List inside. 

C#
public class GenericList<T> : IList<T>
{
    #region Members
    private List<t> m_pItems = null;
    #endregion

Examples of Wrapping

C#
#region IList Methods
        // Summary:
        //     Gets or sets the element at the specified index.
        //
        // Parameters:
        //   index:
        //     The zero-based index of the element to get or set.
        //
        // Returns:
        //     The element at the specified index.
        //
        // Exceptions:
        //   System.ArgumentOutOfRangeException:
        //     index is not a valid index in the System.Collections.Generic.IList<t>.
        //
        //   System.NotSupportedException:
        //     The property is set and the System.Collections.Generic.IList<t> 
        //     is read-only.
        public T this[int index] 
        {
            get { return this.Items[index]; }
            set { this.Items[index] = value; }
        }
        // Summary:
        //     Determines the index of a specific item in the 
        //     System.Collections.Generic.IList<t>.
        //
        // Parameters:
        //   item:
        //     The object to locate in the System.Collections.Generic.IList<t>.
        //
        // Returns:
        //     The index of item if found in the list; otherwise, -1.
        public int IndexOf(T item)
        {
            return this.Items.IndexOf(item);
        }
        //
        // Summary:
        //     Inserts an item to the System.Collections.Generic.IList<t> 
        //     at the specified index.
        //
        // Parameters:
        //   index:
        //     The zero-based index at which item should be inserted.
        //
        //   item:
        //     The object to insert into the System.Collections.Generic.IList<t>.
        //
        // Exceptions:
        //   System.ArgumentOutOfRangeException:
        //     index is not a valid index in the System.Collections.Generic.IList<t>.
        //
        //   System.NotSupportedException:
        //     The System.Collections.Generic.IList<t> is read-only.
        public void Insert(int index, T item)
        {
            OnBeforeItemAdded(this, new GenericItemEventArgs<t>(item));
            this.Items.Insert(index, item);
            OnItemAdded(this, new GenericItemEventArgs<t>(item));
        }
        //
        // Summary:
        //     Removes the System.Collections.Generic.IList<t> 
        //     item at the specified index.
        //
        // Parameters:
        //   index:
        //     The zero-based index of the item to remove.
        //
        // Exceptions:
        //   System.ArgumentOutOfRangeException:
        //     index is not a valid index in the System.Collections.Generic.IList<t>.
        //
        //   System.NotSupportedException:
        //     The System.Collections.Generic.IList<t> is read-only.
        public void RemoveAt(int index)
        {
            OnBeforeItemRemoved(this, new GenericItemEventArgs<t>(this.Items[index]));
            this.Items.RemoveAt(index);
            OnItemRemoved(this, new EventArgs());
        }
        #endregion  

I also add the functions and properties of the ICollection interface, IEnumerable<t> interface, and the IEnumerable interface.

Events

The events I added are the ones I thought were most appropriate for the list.

C#
#region Events
        /// <summary>
        /// Raises when an item is added to the list.
        /// </summary>
        public event EventHandler<GenericItemEventArgs<T>> ItemAdded;
        /// <summary>
        /// Raises before an item is added to the list.
        /// </summary>
        public event EventHandler<GenericItemEventArgs<T>> BeforeItemAdded;
        /// <summary>
        /// Raises when an item is removed from the list.
        /// </summary>
        public event EventHandler<EventArgs> ItemRemoved;
        /// <summary>
        /// Raises before an item is removed from the list.
        /// </summary>
        public event EventHandler<GenericItemEventArgs<T>> BeforeItemRemoved;
        /// <summary>
        /// Raises when the items are cleared from the list.
        /// </summary>
        public event EventHandler<EventArgs> ItemsCleared;
        #endregion

Event Methods

These are the functions that check whether the event is null or not.

C#
#region Event Methods
        /// <summary>
        /// Raises when an Item is added to the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">GenericItemEventArgs</param>
        protected virtual void OnItemAdded(object sender, GenericItemEventArgs<T> e)
        {
            if (ItemAdded != null)
                ItemAdded(sender, e);
        }
        /// <summary>
        /// Raises before an Item is added to the list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">GenericItemEventArgs</param>
        protected virtual void OnBeforeItemAdded
        	(object sender, GenericItemEventArgs<T> e)
        {
            if (BeforeItemAdded != null)
                BeforeItemAdded(sender, e);
        }
        /// <summary>
        /// Raises when an Item is removed from the list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventsArgs</param>
        protected virtual void OnItemRemoved(object sender, EventArgs e)
        {
            if (ItemRemoved != null)
                ItemRemoved(sender, e);
        }
        /// <summary>
        /// Raises before an Item is removed from the list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">GenericItemEventArgs</param>
        protected virtual void OnBeforeItemRemoved
        	(object sender, GenericItemEventArgs<T> e)
        {
            if (BeforeItemRemoved != null)
                BeforeItemRemoved(sender, e);
        }
        /// <summary>
        /// Raises when the Items are cleared from this list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        protected virtual void OnItemsCleared(object sender, EventArgs e)
        {
            if (ItemsCleared != null)
                ItemsCleared(sender, e);
        }
        #endregion

Custom EventArgs

When creating the Events for the functions: Add, Remove, Insert, and RemoveAt I wanted a way for the developer to have access or knowledge of the item being removed or added on the events, so I created a custom EventArgs. I created the GenericItemEventArgs which supplies the developer to the item being added or removed for quick access. 

C#
public class GenericItemEventArgs<T> : EventArgs
    {
        /// <summary>
        /// Item
        /// </summary>
        public T Item { get; private set; }
        /// <summary>
        /// Default constructor
        /// </summary>
        /// 
        public GenericItemEventArgs(T item)
        {
            this.Item = item;
        }
    }

Using the Code 

The list is pretty simple to use. It is identical to the C# List but with events. It has the same function names and everything. If you can use the C# list, you can use this list. 

Examples of code of how to use the list: 

C#
GenericList<int> List = new GenericList<int>()
C#
List.ItemAdded += 	new EventHandler<Tshrove.GenericList.GenericItemEventArgs<int>>
		(List_ItemAdded);
List.ItemRemoved += new EventHandler<EventArgs>(List_ItemRemoved);
List.ItemsCleared += new EventHandler<EventArgs>(List_ItemsCleared);
List.BeforeItemAdded += new EventHandler<Tshrove.GenericList.GenericItemEventArgs<int>>
			(List_BeforeItemAdded);
List.BeforeItemRemoved += new EventHandler
	<Tshrove.GenericList.GenericItemEventArgs<int>>(List_BeforeItemRemoved);
C#
// Adding and Removing Items from the list.
List.Add(5);
List.Remove(5);
List.Add(10);
List.Add(11);
List.Clear();

License

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


Written By
Software Developer U.S. Army / AMRDEC
United States United States
B.S. in Computer/Electrical Engineer from the University of Alabama Huntsville.
M.S.S.E in Software Engineering with Embedded Systems

Comments and Discussions

 
Questionwell done Pin
Member 871442118-Apr-17 11:58
Member 871442118-Apr-17 11:58 
QuestionHow to use the generic list in the asp.net profile? Pin
AnoopGharu31-Oct-14 1:48
AnoopGharu31-Oct-14 1:48 
GeneralpropertyEditor for the propertyGrid Pin
colberg31-Mar-10 7:36
colberg31-Mar-10 7:36 
NewsThanks for Comments Pin
Michael T. Shrove7-Aug-09 15:02
Michael T. Shrove7-Aug-09 15:02 
GeneralMy vote of 2 Pin
tobywf4-Aug-09 6:01
tobywf4-Aug-09 6:01 
GeneralEvent raisers and event arguments, BindingSource Pin
TobiasP5-Aug-09 0:28
TobiasP5-Aug-09 0:28 
I would say this class is a good start, but have some small things left to fix before it can be really useful.

Regarding raising events and event arguments, the standard way is to create the event argument and pass it as the single argument to a protected virtual method OnEventName and let the OnEventName check if there are subscribers. That is, e.g., the way ObservableCollection<T> works. It is true that it creates unneeded objects if there are no subscribers, but it is still the "standard" way.

Another thing I had the displeasure of noticing some time ago (not related to your class) was that the BindingSource class does not consider an IList<T> a list but an IEnumerable, so if you set an IList<T> as the DataSource of a BindingSource and add new items using the BindingSource, they are not inserted into the list. I suppose that is one reason why List<T> implements both IList<T> and the non-generic IList interface.

If you take the messages here to heart, keep fine-tuning the class and post the updates, I'm sure the class will be a very useful addition to many peoples toolbox.
QuestionWhat about System.Collections.ObjectModel.Collection&lt;T&gt; Pin
Steffen Liersch30-Jul-09 22:29
Steffen Liersch30-Jul-09 22:29 
GeneralIntuitive Pin
Stephen Inglish30-Jul-09 8:07
Stephen Inglish30-Jul-09 8:07 
GeneralRe: Intuitive Pin
Michael T. Shrove30-Jul-09 16:37
Michael T. Shrove30-Jul-09 16:37 
GeneralObservableCollection Pin
Wim Reymen27-Jul-09 13:12
Wim Reymen27-Jul-09 13:12 
AnswerRe: ObservableCollection Pin
Michael T. Shrove29-Jul-09 13:24
Michael T. Shrove29-Jul-09 13:24 

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

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