.NET Generics 4.0: Container Patterns and Best Practices

0
110
5 min read

(For more resources on .NET, see here.)

Generic container patterns

There are several generic containers such as List<T>, Dictionary<Tkey,Tvalue>, and so on. Now, let’s take a look at some of the patterns involving these generic containers that show up more often in code.

How these are organized

Each pattern discussed in this article has a few sections. First is the title. This is written against the pattern sequence number. For example, the title for Pattern 1 is One-to-one mapping. The Pattern interface section denotes the interface implementation of the pattern. So anything that conforms to that interface is a concrete implementation of that pattern. For example, Dictionary<TKey,TValue> is a concrete implementation of IDictionary<TKey,TValue>. The Example usages section shows some implementations where TKey and TValue are replaced with real data types such as string or int. The last section, as the name suggests, showcases some ideas where this pattern can be used.

Pattern 1: One-to-one mapping

One-to-one mapping maps one element to another.

Pattern interface

The following is an interface implementation of this pattern:
IDictionary<TKey,Tvalue>

Some concrete implementations

Some concrete implementations of this pattern are as follows:

  • Dictionary<TKey,TValue>
  • SortedDictionary<TKey,TValue>
  • SortedList<TKey,TValue>

Example usages

The following are examples where TKey and TValue are replaced with real data types such as string or int:

  • Dictionary<string,int>
  • SortedDictionary<int,string>
  • SortedList<string,string>
  • Dictionary<string,IClass>

Some situations where this pattern can be used

One-to-one mapping can be used in the following situations:

  • Mapping some class objects with a string ID
  • Converting an enum to a string
  • General conversion between types
  • Find and replace algorithms where the find and replace strings become key and value pairs
  • Implementing a state machine where each state has a description, which becomes the key, and the concrete implementation of the IState interface becomes the value of a structure such as Dictionary<string,IState>

Pattern 2: One-to-many unique value mapping

One-to-many unique value mapping maps one element to a set of unique values.

Pattern interface

The following is an interface implementation of this pattern:
IDictionary<TKey,ISet<Tvalue>>

Some concrete implementations

Some concrete implementations of this pattern are as follows:

  • Dictionary<TKey,HashSet<TValue>>
  • SortedDictionary<TKey,HashSet<TValue>>
  • SortedList<TKey,SortedSet<TValue>>
  • Dictionary<TKey,SortedSet<TValue>>

Example usages

The following are examples where TKey and TValue are replaced with real data types such as string or int:

  • Dictionary<int,HashSet<string>>
  • SortedDictionary<string,HashSet<int>>
  • Dictionary<string,SortedSet<int>>

Some situations where this pattern can be used

One-to-many unique value mapping can be used in the following situations:

  • Mapping all the anagrams of a given word
  • Creating spell check where all spelling mistakes can be pre-calculated and stored as unique values

Pattern 3: One-to-many value mapping

One-to-many value mapping maps an element to a list of values. This might contain duplicates.

Pattern interface

The following are the interface implementations of this pattern:

  • IDictionary<TKey,ICollection<Tvalue>>
  • IDictionary<TKey,Ilist<TValue>>

Some concrete implementations

Some concrete implementations of this pattern are as follows:

  • Dictionary<TKey,List<TValue>>
  • SortedDictionary<TKey,Queue<TValue>>
  • SortedList<TKey,Stack<TValue>>
  • Dictionary<TKey,LinkedList<TValue>>

Example usages

The following are examples where TKey and TValue are replaced with real data types such as string or int:

  • Dictionary<string,List<DateTime>>
  • SortedDictionary<string,Queue<int>>
  • SortedList<int,Stack<float>>
  • Dictionary<string,LinkedList<int>>

Some situations where this pattern can be used

One-to-many value mapping can be used in the following situations:

  • Mapping all the grades obtained by a student. The ID of the student can be the key and the grades obtained in each subject (which may be duplicate) can be stored as the values in a list.
  • Tracking all the followers of a Twitter account. The user ID for the account will be the key and all follower IDs can be stored as values in a list.
  • Scheduling all the appointments for a patient whose user ID will serve as the key.

Pattern 4: Many-to-many mapping

Many-to-many mapping maps many elements of a group to many elements in other groups. Both can have duplicate entries.

Pattern interface

The following are the interface implementations of this pattern:

  • IEnumerable<Tuple<T1,T2,..,ISet<Tresult>>>
  • IEnumerable<Tuple<T1,T2,..,ICollection<Tresult>>>

Some concrete implementations

A concrete implementation of this pattern is as follows:
IList<Tuple<T1,T2,T3,HashSet<TResult>>>

Example usages

The following are examples where TKey and TValue are replaced with real data types such as string or int:

  • List<Tuple<string,int,int,int>>
  • List<Tuple<string,int,int,int,HashSet<float>>>

Some situations where this pattern can be used

Many-to-many mapping can be used in the following situations:

  • If many independent values can be mapped to a set of values, then these patterns should be used. ISet<T> implementations don’t allow duplicates while ICollection<T> implementations, such as IList<T>, do.
  • Imagine a company wants to give a pay hike to its employees based on certain conditions. In this situation, the parameters for conditions can be the independent variable of the Tuples, and IDs of employees eligible for the hike can be stored in an ISet<T> implementation.

For concurrency support, replace non-concurrent implementations with their concurrent cousins. For example, replace Dictionary<TKey,TValue> with ConcurrentDictionary<TKey,TValue>.

LEAVE A REPLY

Please enter your comment!
Please enter your name here