A collection is a way to create and manage groups of related objects. The group of objects can grow and shrink dynamically depending on the requirements of the collection. These include lists, linked lists, dictionaries, arrays, and so on. All collections are classes except array, so it should be declared as a new collection before adding an element to it.
Namespaces in collection
- For the non-generic collection classes and interfaces.
using System.Collections;
For example, Lists, queues, bit arrays, hash tables, and dictionaries.
- For the strongly typed non-generic collection classes.
using System.Collections.Specialized;
- The System.Collections.Specialized namespace contains specialized and strongly typed collections. For example a linked list dictionary, a bit vector, and collections that contain only strings.
- For the Generic collection classes and interfaces.
using System.Collections.Generic;
- The System.Collections.Generic namespace contains interfaces and classes that define generic collections that allow users to create strongly typed collections that provide better type safety and performance than non-generic strongly typed collections.
- For the thread-safe collection classes.
using System.Collections.Concurrent;
- Provides several thread-safe collection classes.
- Proxies and bases for custom collections.
using System.Collections.ObjectModel;
Enumeration
The basic purpose of a collection is to store objects and their content traversal. C# supports this need via a pair of interfaces, IEnumerable and IEnumerator. It allows a different data structure to expose a common traversal mechanism. These concepts are called Enumeration. Enumeration can be used to read the data in the collection, but it can't modify the underlying collection. It only provides iteration over a non-generic collection.
IEnumerable and IEnumerator
IEnumerator is an interface that defines the basic rule or protocol by which elements in a collection are traversed or enumerated. It provides rules for traversal in a forward-only manner. Its namespace is System. Collection. It is the base Interface for all non-generic enumerators.
Properties
- Current: Gets the current element in the collection
- Methods
- MoveNext: Traverses the current element to the next element.
- Reset: It sets the enumerator to its initial position. It is before the first element in the collection.
Declaration of IEnumerable
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
Collections do not implement Enumerators directly. IEnumerable Interface provides them, with Enumerators, as below.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
In this way, IEnumerable provides flexibility in the iteration or traversal logic. Iteration logic can be implemented separately in the other class. So, several consumers can enumerate the collection at once without interfering with each other.
Let's see an example.
public class Test
{
string name = "Ershad";
IEnumerator iterate = name.GetEnumerator();
while (iterate.MoveNext())
{
char val = (char)iterate.Current;
Console.WriteLine(val + ", ");
}
}
Output
E, r, s, h, a, d
But, in the shortcut form.
public class Test
{
string name = "Ershad";
foreach (char val in name)
{
Console.WriteLine(val + ", ");
}
}
IEnumerator and IEnumerable were for the non-generic collections. So, in the extended version, they are implemented in conjunction with a generic type. So, in the generic implementation, they are IEnumerable<T> and IEnumerator<T>.
IEnumerable<T> and IEnumerator<T>
It is the extended generic version of IEnumerator and IEnumerable.
Declaration of IEnumerator<T>
public Interface IEnumerator<T> : IEnumerator, IDisposable
{
T Current { get; }
}
Now, Declaration of IEnumerable<T>.
public Interface IEnumerable<T> :IEnumerable
{
IEnumerator<T>GetEnumerator();
}
Here, the extended or typed version of Current and GetEnumerator() has strengthened static type safety. It avoids the overhead of boxing with value type elements and is very easy for the consumers.
IDisposable
IDisposable allows holding of references to resources and ensures that those resources are released when the enumeration is complete. Its namespace is System.
IDisposable has the following members.
Dispose: It performs operations like freeing, releasing or resetting unmanaged resources.
The garbage collector has no knowledge of unmanaged resources like window handles, open files, streams and so on. The Dispose method of IDisposable interface explicitly releases the unmanaged resources from memory in combination with the Garbage Collector.
Drawbacks of Enumeration Interfaces
Enumeration interfaces only provide forward iteration or traversal mechanism over a collection. They don't provides the following facilities.
- A mechanism to find the size of the collection.
- A way to access a specific member of a collection using an index.
- A search or modify facility in the collection.
So, to provide all these facilities in the collection ICollection, IList and IDictionary array has been introduced.
ICollection or ICollection<T>
It extends the IEnumerable Interface.
It provides the following functionalities for the collection.
- Counts the size of the Collection.
- Checks whether or not an item exists in the collection (Contain).
- Copies the collection into an array (ToArray).
- Determines whether the collection is read-only (IsReadOnly).
- If a collection is not read-only then it also provides functionalities to Add, Remove and clear items from the collection.
A non-generic collection doesn't provide functionalities for altering the list or checking the element membership.
Declaration of ICollection<T>
public Interface ICollection<T> : IEnumerable<T>, IEnumerable
{
int Count { get; }
bool Contains (T item );
void CopyTo (T[] array, int arrayIndex);
bool IsReadOnly { get; }
void Add (T item);
bool Remove (T item);
void Clear();
}
IList or IList<T>
IList<T> inherits from ICollection<T> and IEnumerable<T>.
It provides the following functionalities for the collections.
- To read or write elements of a collection by position or indexes.
- To insert or remove an element of a collection by position or indexes.
- The Add() method on the IList Interface returns an integer that is the index of a newly added item. But the Add() method of ICollection<T> returns a void type.
Declaration of IList<T>
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
{
object this[int index] { get; set; }
bool IsFixedSize { get; }
bool IsReadOnly { get; }
int Add(object value);
void Clear();
bool Contains(object value);
int IndexOf(object value);
void Remove(object value);
void RemoveAt(int index);
}
Dictionaries
A Dictionary is a collection in which each element is a Key and Value pair. The protocol of a Dictionary has been defined by the IDictionary and IDictionary<TKey, TValue> Interfaces.
It provides the following functionalities for the collection:
It is used for the lookups and sorted fields.
Declaration of IDictionary<TKey, TValue>
public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable
{
bool ContainsKey(TKey key);
bool TryGetValue(TKey key, out TValue value);
void Add(TKey key, TValue value);
bool Remove(TKey key);
TValue this[TKey key] { get; set; } // Main indexer - by key
ICollection<TKey> Keys { get; } // Returns keys
ICollection<TValue> Values { get; } // Returns values
}
The non-generic version of IDictionary<TKey, TValue> is called Hashtable.
Drawbacks of Collection Classes
Collection classes are very good in the sense that they can be directly instantiated but they don't allow the following facilities.
A Collection class does not allow us to control what happens when an item is added or removed from the collection.
This facility is required in the following cases.
- To fire an even when an item is added or removed.
- To update properties after adding or removing an item from a collection.
- To detect an illegal add/remove operation or violation of a project's business rule.
So, to fulfill these requirements the Customizable collection has been introduced.