web analytics

Understanding Generic Collections in C#

Options

codeling 1595 - 6639
@2016-02-24 20:15:48

.NET framework introduces a set of generic collections in the System.Collections.Generic namespace. For example, there are generic Stack<T> and a generic Queue<T> classes. The Dictionary<K,T> data structure is equivalent to the non-generic HashTable, and there is also a SortedDictionary<K,T> class somewhat like SortedList. The class List<T> is analogous to the non-generic ArrayList. The following table maps the main types of System.Collections.Generic to those of System.Collections.

System.Collections.Generic System.Collections
Comparer<T> Comparer
Dictionary<K,T> HashTable
LinkedList<T> -
List<T> ArrayList
Queue<T> Queue
SortedDictionary<K,T> SortedList
Stack<T> Stack
ICollection<T> ICollection
IComparable<T> System.IComparable
IDictionary<K,T> IDictionary
IEnumerable<T> IEnumerable
IEnumerator<T> IEnumerator
IList<T> IList

All the generic collections in System.Collections.Generic also implement the generic IEnumerable<T> interface, defined as:

public interface IEnumerable<T>
{
   IEnumerator<T> GetEnumerator();
} 
public interface IEnumerator<T> : IDisposable
{
   T Current{get;}
   bool MoveNext();
}
      

Briefly, IEnumerable<T> provides access to the IEnumerator<T> iterator interface, used for abstracted iterations over the collection. All the collections implement IEnumerable<T> on a nested struct, where the generic type parameter T is the type the collection stores.

Of particular interest is the way the dictionary collections define their iterators. A dictionary is actually a collection of not one but two types of generic parameter—the key and the value. System.Collection.Generic provides a generic struct called KeyValuePair<K,V> defined as:

struct KeyValuePair<K,V>
{
   public KeyValuePair(K key,V value);
   public K Key(get;set;)
   public V Value(get;set;)
}

KeyValuePair<K,V> simply stores a pair of a generic key and a generic value. This struct is what the dictionary manages as a collection, and what it uses for its implementation of IEnumerable<T>. The Dictionary class specifies the generic KeyValuePair<K,V> structure as the item argument for IEnumerable<T> and ICollection<T>:

public class Dictionary<K,T> : IEnumerable<KeyValuePair<K,T>>, 
                               ICollection<KeyValuePair<K,T>>, 
                               //More interfaces
{...} 

The key and value type parameters used in KeyValuePair<K,V> are of course the dictionary's own generic key and value type parameters. You can certainly do the same in your own generic data structures that use pairs of keys and values. For example:

public class LinkedList<K,T> : IEnumerable<KeyValuePair<K,T>> where K : IComparable<K>
{...} 
@2016-02-24 21:02:19

List<T>

The List<T> class is the generic equivalent of the ArrayList class. It implements the IList<T> generic interface using an array whose size is dynamically increased as required.

The List<T> class uses both an equality comparer and an ordering comparer.

  • Methods such as Contains, IndexOf, LastIndexOf, and Remove use an equality comparer for the list elements. The default equality comparer for type T is determined as follows. If type T implements the IEquatable<T> generic interface, then the equality comparer is the Equals(T) method of that interface; otherwise, the default equality comparer is Object.Equals(Object).

  • Methods such as BinarySearch and Sort use an ordering comparer for the list elements. The default comparer for type T is determined as follows. If type T implements the IComparable<T> generic interface, then the default comparer is the CompareTo(T) method of that interface; otherwise, if type T implements the nongeneric IComparable interface, then the default comparer is the CompareTo(Object) method of that interface. If type T implements neither interface, then there is no default comparer, and a comparer or comparison delegate must be provided explicitly.

The List<T> is not guaranteed to be sorted. You must sort the List<T> before performing operations (such as BinarySearch) that require the List<T> to be sorted.

Elements in this collection can be accessed using an integer index. Indexes in this collection are zero-based.

List<T> accepts null as a valid value for reference types and allows duplicate elements.

@2016-02-25 20:40:35

CollectionBase<T> Class

As you know, CollectionBase in the namespace System.Collections is the base class to help you create your own strong typed collection class. The following CollectionBase<T> class is the generic version of the CollectionBase class.

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace Canaware.ApplicationFramework.Core.Collections
{
    public class CollectionBase<T> :
        IList<T>, IList,
        ICollection<T>, ICollection,
        IEnumerable<T>, IEnumerable
    {
        private List<T> _list;

        //private bool sorted = true;
        public CollectionBase()
        {
        }

        public CollectionBase(int capacity)
        {
            _list = new List<T>(capacity);
        }
        [ComVisible(false)]
        public int Capacity
        {
            get
            {
                if (this._list == null)
                {
                    this._list = new List<T>();
                }

                return this._list.Capacity;
            }
            set
            {
                if (this._list == null)
                {
                    this._list = new List<T>();
                }
                this._list.Capacity = value;
            }
        }

        protected List<T> InnerList
        {
            get
            {
                if (this._list == null)
                {
                    this._list = new List<T>();
                }
                return this._list;
            }
        }

        public void Clear()
        {
            this.OnClear();
            this.InnerList.Clear();

            this.OnClearComplete();
        }
        public void RemoveAt(int index)
        {
            T value = InnerList[index];

            OnValidate(value);
            OnRemove(index, value);
            InnerList.RemoveAt(index);

            OnRemoveComplete(index, value);
        }
        public int Count
        {
            get
            {
                return InnerList.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return ((ICollection<T>)InnerList).IsReadOnly;
            }
        }
        #region Notification Events

        protected virtual void OnClear()
        {
        }
        protected virtual void OnClearComplete()
        {
        }

        protected virtual void OnInsert(int index, T value)
        {
        }
        protected virtual void OnInsertComplete(int index, T value)
        {
        }

        protected virtual void OnRemove(int index, T value)
        {
        }
        protected virtual void OnRemoveComplete(int index, T value)
        {
        }

        protected virtual void OnSet(int index, T oldValue, T value)
        {
        }
        protected virtual void OnSetComplete(int index, T oldValue, T value)
        {
        }

        protected virtual void OnValidate(T value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
        }
        #endregion


        #region IList<T> Members
        int IList<T>.IndexOf(T item)
        {
            return InnerList.IndexOf(item);
        }

        void IList<T>.Insert(int index, T item)
        {
            OnValidate(item);
            OnInsert(index, item);
            InnerList.Insert(index, item);

            try
            {
                OnInsertComplete(index, item);
            }
            catch
            {
                InnerList.RemoveAt(index);
                throw;
            }
        }

        T IList<T>.this[int index]
        {
            get
            {
                return InnerList[index];
            }

            set
            {
                if (index < 0 || index >= InnerList.Count)
                {
                    throw new ArgumentOutOfRangeException("index");
                }
                OnValidate(value);

                T oldValue = InnerList[index];
                OnSet(index, oldValue, value);

                InnerList[index] = value;
                try
                {
                    OnSetComplete(index, oldValue, value);
                }
                catch
                {
                    InnerList[index] = oldValue;

                    throw;
                }
            }
        }
        #endregion


        #region ICollection<T> Members
        void ICollection<T>.Add(T item)
        {
            OnValidate(item);

            int index = InnerList.Count;
            OnInsert(index, item);

            InnerList.Add(item);
            try
            {
                OnInsertComplete(index, item);
            }
            catch
            {
                InnerList.RemoveAt(index);

                throw;
            }
        }

        bool ICollection<T>.Contains(T item)
        {
            return InnerList.Contains(item);
        }
        void ICollection<T>.CopyTo(T[] array, int arrayIndex)
        {
            InnerList.CopyTo(array, arrayIndex);
        }

        bool ICollection<T>.Remove(T item)
        {
            OnValidate(item);
            int index = InnerList.IndexOf(item);

            if (index < 0) return false;
            OnRemove(index, item);

            InnerList.Remove(item);
            OnRemoveComplete(index, item);

            return true;
        }
        #endregion


        #region IEnumerable<T> Members
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return InnerList.GetEnumerator();
        }

        #endregion
        #region IList Members

        int IList.Add(object value)
        {
            OnValidate((T)value);
            int index = InnerList.Count;

            OnInsert(index, (T)value);
            index = ((IList)InnerList).Add(value);

            try
            {
                OnInsertComplete(index, (T)value);
            }
            catch
            {
                InnerList.RemoveAt(index);
                throw;
            }

            return index;
        }
        bool IList.Contains(object value)
        {
            return ((IList)InnerList).Contains(value);
        }

        int IList.IndexOf(object value)
        {
            return ((IList)InnerList).IndexOf(value);
        }
        void IList.Insert(int index, object value)
        {
            OnValidate((T)value);
            OnInsert(index, (T)value);

            ((IList)InnerList).Insert(index, value);
            try
            {
                OnInsertComplete(index, (T)value);
            }
            catch
            {
                InnerList.RemoveAt(index);

                throw;
            }
        }
        bool IList.IsFixedSize
        {
            get
            {
                return ((IList)InnerList).IsFixedSize;
            }
        }

        void IList.Remove(object value)
        {
            OnValidate((T)value);
            int index = InnerList.IndexOf((T)value);

            if (index < 0)
            {
                throw new ArgumentException("The element cannot be found.", "value");
            }
            OnRemove(index, (T)value);

            ((IList)InnerList).Remove(value);
            OnRemoveComplete(index, (T)value);
        }

        object IList.this[int index]
        {
            get
            {
                return InnerList[index];
            }
            set
            {
                if (index < 0 || index >= InnerList.Count)
                {
                    throw new ArgumentOutOfRangeException("index");
                }

                OnValidate((T)value);
                T oldValue = InnerList[index];

                OnSet(index, oldValue, (T)value);
                InnerList[index] = (T)value;

                try
                {
                    OnSetComplete(index, oldValue, (T)value);
                }
                catch
                {
                    InnerList[index] = oldValue;
                    throw;
                }
            }
        }

        #endregion
        #region ICollection Members

        void ICollection.CopyTo(Array array, int index)
        {
            ((ICollection)InnerList).CopyTo(array, index);
        }
        bool ICollection.IsSynchronized
        {
            get
            {
                return ((ICollection)InnerList).IsSynchronized;
            }
        }

        object ICollection.SyncRoot
        {
            get
            {
                return ((ICollection)InnerList).SyncRoot;
            }
        }
        #endregion


        #region IEnumerable Members
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)InnerList).GetEnumerator();
        }

        #endregion
    }
}
@2016-03-06 10:13:19

Programmatic Implementation Model

This model requires that for each section attribute you create a property to get and/or set its value and add it to the internal property collection of the underlying ConfigurationElement base class.

The following example shows how to implement a custom section programmatically.

// Define a custom section.
// The CustomSection type allows to define a custom section
// programmatically.
public sealed class CustomSection :
    ConfigurationSection
{
    // The collection (property bag) that contains
    // the section properties.
    private static ConfigurationPropertyCollection _Properties;

    // Internal flag to disable
    // property setting.
    private static bool _ReadOnly;

    // The FileName property.
    private static readonly ConfigurationProperty _FileName =
        new ConfigurationProperty("fileName",
        typeof(string),"default.txt",
        ConfigurationPropertyOptions.IsRequired);

    // The MaxUsers property.
    private static readonly ConfigurationProperty _MaxUsers =
        new ConfigurationProperty("maxUsers",
        typeof(long), (long)1000,
        ConfigurationPropertyOptions.None);

    // The MaxIdleTime property.
    private static readonly ConfigurationProperty _MaxIdleTime =
        new ConfigurationProperty("maxIdleTime",
        typeof(TimeSpan), TimeSpan.FromMinutes(5),
        ConfigurationPropertyOptions.IsRequired);

    // CustomSection constructor.
    public CustomSection()
    {
        // Property initialization
        _Properties =
            new ConfigurationPropertyCollection();

        _Properties.Add(_FileName);
        _Properties.Add(_MaxUsers);
        _Properties.Add(_MaxIdleTime);
   }


    // This is a key customization.
    // It returns the initialized property bag.
    protected override ConfigurationPropertyCollection Properties
    {
        get
        {
            return _Properties;
        }
    }


    private new bool IsReadOnly
    {
        get
        {
            return _ReadOnly;
        }
    }

    // Use this to disable property setting.
    private void ThrowIfReadOnly(string propertyName)
    {
        if (IsReadOnly)
            throw new ConfigurationErrorsException(
                "The property " + propertyName + " is read only.");
    }


    // Customizes the use of CustomSection
    // by setting _ReadOnly to false.
    // Remember you must use it along with ThrowIfReadOnly.
    protected override object GetRuntimeObject()
    {
        // To enable property setting just assign true to
        // the following flag.
        _ReadOnly = true;
        return base.GetRuntimeObject();
    }


    [StringValidator(InvalidCharacters = " ~!@#$%^&*()[]{}/;'\"|\\",
        MinLength = 1, MaxLength = 60)]
    public string FileName
    {
        get
        {
            return (string)this["fileName"];
        }
        set
        {
            // With this you disable the setting.
            // Remember that the _ReadOnly flag must
            // be set to true in the GetRuntimeObject.
            ThrowIfReadOnly("FileName");
            this["fileName"] = value;
        }
    }

    [LongValidator(MinValue = 1, MaxValue = 1000000,
        ExcludeRange = false)]
    public long MaxUsers
    {
        get
        {
            return (long)this["maxUsers"];
        }
        set
        {
            this["maxUsers"] = value;
        }
    }

    [TimeSpanValidator(MinValueString = "0:0:30",
        MaxValueString = "5:00:0",
        ExcludeRange = false)]
    public TimeSpan MaxIdleTime
    {
        get
        {
            return  (TimeSpan)this["maxIdleTime"];
        }
        set
        {
            this["maxIdleTime"] = value;
        }
    }
}

The following example is an excerpt of the configuration file as it applies to the previous example.

<?xml version="1.0" encoding="utf-8"?>

<configuration>


<configSections>

<section name="CustomSection" type="Samples.AspNet. CustomSection, CustomConfigurationSection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" allowDefinition="Everywhere" allowExeDefinition="MachineToApplication" restartOnExternalChanges="true" />

</configSections>

<CustomSection fileName="default.txt" maxUsers="1000" maxIdleTime="00:15:00" />


</configuration>

Comments

You must Sign In to comment on this topic.


© 2024 Digcode.com