Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

EnumConstrainedDictionary

2.80/5 (2 votes)
5 Jan 2016CPOL3 min read 10.4K   19  
A Dictionary that is constrained to hold only the defined members of an enumeration

Introduction

This article presents a dictionary that is constrained to hold only those keys which are defined in an enumeration; those keys are automatically pre-filled by the constructor. No keys may be removed, and no keys may be added. Furthermore, the Dictionary may be made read-only so the values can't be changed either.

Background

OK, why would you want to do that?

I am now using this class in my ParsedCommandLine[^] class, where it holds the values of the "switches" from the command line. There, it replaces the use of a more generic ReadOnlyDictionary.

However, the more immediate impetus for writing this class is this Tip: OperationResult Class in Support of Multiple Return Values from a Method[^] which presents a solution for a similar situation -- that of returning a Dictionary of result data.

Using the code

OK, so just how would this class be used in that situation?

Consider that this class constrains the Dictionary to the members of an enumeration. That enumeration must be accessible by both the calling and called code and it acts as an explicit contract between the sections of code. The called code says that it will return a Dictionary that contains the members of a particular enumeration and it follows through on that promise. The calling code can then confidently access the items in the Dictionary.

With the code in the referenced Tip, the caller has no guarantee of what keys are in the Dictionary.

As an example, I'll use something like that in the referenced Tip. Let's say that we're writing a method that will accept a string and attempt to extract a URI and a port number from it. The result of such an operation may yield the following four values, as defined in an enumeration:

C#
public enum ParseUriResult
{
  Success
,
  Message  
,
  ServerUri
,
  Port        
}

The method can then be defined something like:

C#
public static EnumConstrainedDictionary<ParseUriResult,object>
ParseUri ( string Candidate )
{
  EnumConstrainedDictionary<ParseUriResult,object> result = 
    new EnumConstrainedDictionary<ParseUriResult,object>() ;

  try
  {
    string uri  = null ;
    int    port = 0    ;

    // Do whatever it takes to extract the data

    result [ ParseUriResult.Success   ] = true ;
    result [ ParseUriResult.ServerUri ] = uri ;
    result [ ParseUriResult.Port      ] = port ;
  }
  catch ( System.Exception ex )
  {
    result [ ParseUriResult.Success   ] = false ;
    result [ ParseUriResult.Message   ] = ex.Message ;
  }

  result.IsReadOnly = true ;

  return ( result ) ;
}

And the caller can access the returned data something like:

C#
EnumConstrainedDictionary<ParseUriResult,object> uri = ParseUri ( foo ) ;

if ( (bool) uri [ ParseUriResult.Success ] )
{
  connect ( (string) uri [ ParseUriResult.ServerUri ] , (int) uri [ ParseUriResult.Port ] ) ;
}
else
{
  log ( uri [ ParseUriResult.Message ] ) ;
}

That's rather ugly, but it's OK for something you don't use very frequently, and which is burried deep within a library. Just for exercise, let's try to clean it up a little bit.

A more mature technique

Define a class that derives from EnumConstrainedDictionary and which contains the enumeration:

C#
public sealed class ParseUriResult : EnumConstrainedDictionary<ParseUriResult.Field,object>
{
  public enum Field
  {
    Success
  ,
    Message  
  ,
    ServerUri
  ,
    Port        
  }
}

And then use it like this:

C#
public static ParseUriResult
ParseUri ( string Candidate )
{
  ParseUriResult result = 
    new ParseUriResult() ;

  try
  {
    string uri  = null ;
    int    port = 0    ;

    // Do whatever to extract the data

    result [ ParseUriResult.Field.Success   ] = true ;
    result [ ParseUriResult.Field.ServerUri ] = uri ;
    result [ ParseUriResult.Field.Port      ] = port ;
  }
  catch ( System.Exception ex )
  {
    result [ ParseUriResult.Field.Success ] = false ;
    result [ ParseUriResult.Field.Message ] = ex.Message ;
  }

  result.IsReadOnly = true ;

  return ( result ) ;
}

And like this:

C#
public static void
ConnectUri
(
  System.Net.Sockets.Socket Socket
,
  string                    Uri
)
{
  ParseUriResult uri = ParseUri ( Uri ) ;

  if ( (bool) uri [ ParseUriResult.Field.Success ] )
  {
    Socket.Connect ( (string) uri [ ParseUriResult.Field.ServerUri ] , (int) uri [ ParseUriResult.Field.Port ] ) ;
  }
  else
  {
    System.Console.WriteLine ( uri [ ParseUriResult.Field.Message ] ) ;
  }
}

That seems much cleaner to me.

EnumConstrainedDictionary

The class wraps a System.Collections.Generic.Dictionary<Tkey,Tvalue> and implements System.Collections.Generic.IDictionary<Tkey,Tvalue>.

Most of the members are simply passed through to the Dictionary -- e.g. Count .

Several are simply marked Obsolete and will throw a System.InvalidOperationException if they're ever called -- e.g. Add and Remove .

I won't bore you with those details.

Constructor

The constructor gets the defined values from the enumeration and adds them to the Dictionary.

C#
public EnumConstrainedDictionary
(
)
{
    /* Instantiate the dictionary */
    this.data = new System.Collections.Generic.Dictionary<Tkey,Tvalue>() ;

    /* Get all the defined members of the enumeration */
    Tkey[] a = (Tkey[]) System.Enum.GetValues ( keytype ) ;

    /* Put all the defined members in the dictionary */
    for ( int i = 0 ; i < a.Length ; i++ )
    {
        this.data [ a [ i ] ] = default(Tvalue) ;
    }

    return ;
}

IsReadOnly

Is used to get and set the locked state of the instance. Once you've set locked to true, you can't change the values in the Dictionary.

C#
private bool locked = false ;

public virtual bool
IsReadOnly
{
    get
    {
        return ( this.locked ) ;
    }

    set
    {
        if ( this.locked && !value )
        {
            throw ( new System.InvalidOperationException ( "This instance is read-only" ) ) ;
        }

        this.locked = value ;

        return ;
    }
}

Indexer

The indexer is implemented like this:

C#
public virtual Tvalue
this
[
    Tkey Key
]
{
    get
    {
        if ( !this.data.ContainsKey ( Key ) )
        {
            throw ( new System.ArgumentOutOfRangeException ( "Key" , Key , "That Key does not exist" ) ) ;
        }

        return ( this.data [ Key ] ) ;
    }

    set
    {
        if ( !this.data.ContainsKey ( Key ) )
        {
            throw ( new System.ArgumentOutOfRangeException ( "Key" , Key , "That Key does not exist" ) ) ;
        }

        if ( this.locked )
        {
            throw ( new System.InvalidOperationException ( "This instance is read-only" ) ) ;
        }

        this.data [ Key ] = value ;

        return ;
    }
}

Conclusion

This class probably won't be used much, but I think it fits a rather small niche very nicely.

This has not been extensively tested. My ParsedCommandLine class uses a PIEBALD.Type.EnumConstrainedDictionary<T,string> which doen't really stress the code.

History

2016-01-04 First submitted

License

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