September 13, 2017

CSV Files with Linq

An example of using Linq to parse a CSV file and write data to a TSV file. The beauty of the approach shown here is the file is read one line at a time when parsing, you do not have to load the whole file to process the data, and written one line at a time when writing. Here is a sample piece of the CSV file (note the first line is a header line not a data line):
id,edsm_id,name,x,y,z,population,is_populated,government_id,government,allegiance_id,allegiance,state_id,state,security_id,security,primary_economy_id,primary_economy,power,power_state,power_state_id,needs_permit,updated_at,simbad_ref,controlling_minor_faction_id,controlling_minor_faction,reserve_type_id,reserve_type
17,60,"10 Ursae Majoris",0.03125,34.90625,-39.09375,0,0,176,None,5,None,80,None,16,Low,10,None,,,,0,1497906646,"10 Ursae Majoris",,,,
24,12009,"11 Bootis",-49.40625,285.25,65.21875,0,0,176,None,5,None,80,None,16,Low,10,None,,,,0,1474116394,"11 Bootis",,,,
26,13308,"11 Mu Aurigae",-30,0.75,-150.03125,0,0,176,None,5,None,80,None,16,Low,10,None,,,,0,1497806946,,,,,


Want to parse some of these lines into this structure:
public class BaseSystemRaw
{
    public int id { get; set; }
    public string name { get; set; }
    public float x { get; set; }
    public float y { get; set; }
    public float z { get; set; }

    public override string ToString()
    {
        return "id=" + id + ", name=" + name + 
               ", location=( " + x + ", " + y + ", " + z + " )";
    }
}
Here is a test harness:
[ TestFixture ]
class ParseCsvFilesWithLinqTests
{
    [ Test ]
    public void ParseCsvFileTest()
    {
        // Skip(1) will skip the header line
        // This commented out version will only use the first 10 lines,
        // great for debugging
        // var baseSystemRaws = File.ReadLines(TestFilePaths.SystemsFilePath).
        //     Take(10).Skip(1).Select(line => ExtractBaseSystemRaw(line));
        var baseSystemRaws = File.ReadLines( TestFilePaths.SystemsFilePath ).
                Skip( 1 ).Select( line => ExtractBaseSystemRaw( line ) );
        DoSomethingWith( baseSystemRaws );
    }

    BaseSystemRaw ExtractBaseSystemRaw(
        string line )
    {
        var parts = line.Split( ',' );
        var sysRaw = new BaseSystemRaw();
        int tmp;
        if ( int.TryParse( parts[ 0 ], out tmp ) )
        {
            sysRaw.id = tmp;
        }
        // Ignore parts[1] edsm_id
        sysRaw.name = parts[ 2 ].Trim().Replace( "\"", "" );
        float ftmp;
        if ( float.TryParse( parts[ 3 ], out ftmp ) )
        {
            sysRaw.x = ftmp;
        }
        if ( float.TryParse( parts[ 4 ], out ftmp ) )
        {
            sysRaw.y = ftmp;
        }
        if ( float.TryParse( parts[ 5 ], out ftmp ) )
        {
            sysRaw.z = ftmp;
        }
        // Ignore other parts
        return sysRaw;
    }

    private void DoSomethingWith(
        IEnumerable<BaseSystemRaw> baseSystemRaws )
    {
        foreach ( var entry in baseSystemRaws )
        {
            Trace.WriteLine( entry );
        }
    }
}
Now write the data to a TSV file.
[ TestFixture ]
class WriteTsvFilesWithLinqTests
{
    [ Test ]
    public void WriteTsvFileTest()
    {
        const string separator = "\t";
        var headers = new[] { "Id", "System", "x", "y", "z" };
        // Skip(1) will skip the header line
        // This version will only use the first 4 lines, great for debugging, 
        // remove the Take(4) to process the whole file
        var baseSystemRaws = File.ReadLines( TestFilePaths.SystemsFilePath ).
            Take(4).Skip(1).Select( line => ExtractBaseSystemRaw( line ) );
        
        // Use Enumerable.Concat to add the header string
        File.WriteAllLines( TestFilePaths.BaseSystemsTsvFilePath,
            Enumerable.Concat( new[] { string.Join( separator, headers ) },
                baseSystemRaws.Select(sys => sys != null ? 
                    CoreSystemRawToTsv( separator, sys ) : "")));
    }

    private string CoreSystemRawToTsv(
        BaseSystemRaw sys )
    {
        string line = string.Join( "\t", sys.id, sys.name, sys.x.ToString( "0.0000" ),
            sys.y.ToString( "0.0000" ), sys.z.ToString( "0.0000" ) );
        return line;
    }
}

September 5, 2017

C# HashSet

Best to demonstrate what some of the methods do with some unit tests. In particular what does 'SymmetricExceptWith' do?
[ TestFixture ]
public class HashSetTests
{
    private HashSet<int> integerSet1;
    private HashSet<int> integerSet1Copy;
    private HashSet<int> integerSet2;

    [ SetUp ]
    public void Setup()
    {
        integerSet1 = new HashSet<int> { 1, 2, 3 };
        integerSet2 = new HashSet<int> { 2, 3, 4 };
        integerSet1Copy = new HashSet<int> { 2, 3, 1 };
    }

    [ Test ]
    // The SetEquals method ignores duplicate entries and 
    // the order of elements in the other parameter.
    public void SetEqualsTest()
    {
        var integerList = new List<int> { 2, 3, 1, 3, 2 };

        Assert.That( !integerSet1.SetEquals( integerSet2 ) );
        Assert.That( integerSet1.SetEquals( integerSet1Copy ) );
        Assert.That( integerSet1.SetEquals( integerList ) );
    }

    [ Test ]
    public void IntersectWithTest() // All elements common to both sets
    {
        integerSet1.IntersectWith( integerSet2 );
        Assert.That( !integerSet1.Contains( 1 ) );
        Assert.That( integerSet1.Contains( 2 ) );
        Assert.That( integerSet1.Contains( 3 ) );
        Assert.That( !integerSet1.Contains( 4 ) );
    }

    [ Test ]
    public void UnionWithTest() // All elements in both sets
    {
        integerSet1.UnionWith( integerSet2 );
        Assert.That( integerSet1.Contains( 1 ) );
        Assert.That( integerSet1.Contains( 2 ) );
        Assert.That( integerSet1.Contains( 3 ) );
        Assert.That( integerSet1.Contains( 4 ) );
    }

    [ Test ]
    public void SymmetricExceptWithTest() // All elements not common to both sets
    {
        integerSet1.SymmetricExceptWith( integerSet2 );
        Assert.That( integerSet1.Contains( 1 ) );
        Assert.That( !integerSet1.Contains( 2 ) );
        Assert.That( !integerSet1.Contains( 3 ) );
        Assert.That( integerSet1.Contains( 4 ) );
    }
}

Here are some extension methods that maintain the original values of the sets (the existing methods modify the calling set)
public static class HashSetExtensions
{
	public static HashSet<T> Union<T>(
        this HashSet<T> hashSet1, IEnumerable<T> hashSet2)
	{
		var res = new HashSet<T>(hashSet1, hashSet1.Comparer);
		res.UnionWith(hashSet2);
		return res;
	}

	public static HashSet<T> Intersecting<T>(
        this HashSet<T> hashSet1, IEnumerable<T> hashSet2)
	{
		var res = new HashSet<T>(hashSet1, hashSet1.Comparer);
		res.IntersectWith(hashSet2);
		return res;
	}

	public static HashSet<T> RemoveAnyFrom<T>(
        this HashSet<T> hashSet1, IEnumerable<T> hashSet2)
	{
		var res = new HashSet<T>(hashSet1, hashSet1.Comparer);
		res.ExceptWith(hashSet2);
		return res;
	}

	public static HashSet<T> NotIntersecting<T>(
        this HashSet<T> hashSet1, IEnumerable<T> hashSet2)
	{
		var res = new HashSet<T>(hashSet1, hashSet1.Comparer);
		res.SymmetricExceptWith(hashSet2);
		return res;
	}
}
and some tests for them
string[] names1 = new string[] {
	"banana","apple","pear","naranja","grapes","mango"
};

string[] names2 = new string[] {
	"advocado","lemon","Pear","lime","banana","passion fruit"
};

HashSet<string> hSetN1 = new HashSet<string>(names1, StringComparer.OrdinalIgnoreCase);
HashSet<string> hSetN2 = new HashSet<string>(names2, StringComparer.OrdinalIgnoreCase);

var res = hSetN1.Union(hSetN2);
var res2 = hSetN1.Intersecting(hSetN2);
var res3 = hSetN1.RemoveAnyFrom(hSetN2);
var res4 = hSetN1.NotIntersecting(hSetN2);

July 28, 2017

An Alternative Queue Class

Here is an alternative generic Queue class. I created it as I constantly found when using a queue class that Enqueue and Dequeue were never enough. There was always a need for a remove an item and a need to iterate through all the items in a queue. The original basic source for the queue class is here.
public class Node< T >
{
    public T Data { get; set; }
    public Node<T> Next { get; set; }

    public Node( T data )
    {
        this.Data = data;
    }
}

public class Queue2< T >
{
    private Node<T> _head;
    private Node<T> _tail;
    private int _count;
    private readonly IEqualityComparer<T> _comparator;

    public Queue2()
    {
        _comparator = EqualityComparer<T>.Default;
    }

    public Queue2( IEqualityComparer<T> comparator )
    {
        _comparator = comparator;
    }

    public void Enqueue(
        T data )
    {
        Node<T> _newNode = new Node<T>( data );
        if ( _head == null )
        {
            _head = _newNode;
            _tail = _head;
        }
        else
        {
            _tail.Next = _newNode;
            _tail = _tail.Next;
        }
        ++_count;
    }

    public T Dequeue()
    {
        if ( _head == null )
        {
            throw new Exception( "Queue is Empty" );
        }
        T _result = _head.Data;
        _head = _head.Next;
        --_count;
        return _result;
    }

    public void Remove(
        T item )
    {
        Node<T> previous = null, cursor = _head;
        bool found = false;
        while ( ( cursor != null ) && !found )
        {
            found = _comparator.Equals( cursor.Data, item );
            if ( found )
            {
                if ( previous == null )
                {
                    _head = cursor.Next;
                }
                else
                {
                    previous.Next = cursor.Next;
                }
                --count;
            }
            else
            {
                previous = cursor;
                cursor = cursor.Next;
            }
        }
    }

    public void Clear()
    {
        _head = _tail = null;
        count = 0;
    }

    public IEnumerable<T> GetEnumerator()
    {
        Node<T> cursor = _head;
        while ( cursor != null )
        {
            yield return cursor.Data;
            cursor = cursor.Next;
        }
    }

    public int Count
    {
        get { return this._count; }
    }
}

July 20, 2017

A String.Split() That Returns An IEnumerable

A version of Split string that returns an IEnumerable<string> rather than an array. Only single separate string is allowed, it is very rare that an array of separator strings is required.
  
public static class StringSplitExtensions
{
  
  public static IEnumerable<string> Split2(
    this string targetStr,
    string separator )
  {
    if ( targetStr == null )
      yield break;

    if ( string.IsNullOrEmpty( separator ) )
    {
      yield return targetStr;
    }
    else
    {
      string str = targetStr;
      bool finished = false;
      int startIx = 0;
      int sepLength = separator.Length;
      while ( !finished )
      {
        var ix = str.IndexOf( separator, 
            StringComparison.OrdinalIgnoreCase );
        if ( ix == -1 )
        {
          finished = true;
          yield return str;
        }
        else
        {
          string res = str.Substring( 0, ix );
          yield return res;
          startIx = ix + sepLength;
          str = str.Substring( startIx, str.Length - startIx );
        }
      }
    }
  }
}



[ Test ]
public void SplitTest()
{
  string test = "012 3456\t7890\ta  aaa\tbbbb\t";

  var res2 = test.Split2( "\t" ).ToArray();
  var norm2 = test.Split( '\t' );
  Assert.IsTrue( AreTheSame( res2, norm2 ) );

  var res3 = "".Split2( "\t" ).ToArray();
  var norm3 = "".Split( '\t' );
  Assert.IsTrue( AreTheSame( res3, norm3 ) );

  var res4 = "\t".Split2( "\t" ).ToArray();
  var norm4 = "\t".Split( '\t' );
  Assert.IsTrue( AreTheSame( res4, norm4 ) );

  var res5 = "\t\t".Split2( "\t" ).ToArray();
  var norm5 = "\t\t".Split( '\t' );
  Assert.IsTrue( AreTheSame( res5, norm5 ) );

  var res6 = "a\t".Split2( "\t" ).ToArray();
  var norm6 = "a\t".Split( '\t' );
  Assert.IsTrue( AreTheSame( res6, norm6 ) );

  var res7 = "\ta".Split2( "\t" ).ToArray();
  var norm7 = "\ta".Split( '\t' );
  Assert.IsTrue( AreTheSame( res7, norm7 ) );

  // Works the same as a standard Split string except in 
  // the case where the Splitter string is an empty string
  var norm8 = test.Split();
  var res8 = test.Split2( "" ).ToArray();
  Assert.IsFalse( AreTheSame( res8, norm8 ) );
}


// Checks whether 2 string arrays are exactly the same, ie. they have
// the same length and the same values at each array entry
public static bool AreTheSame(
string[] stringArray1,
string[] stringArray2)
{
  bool areTheSame = (stringArray1.Length == stringArray2.Length);
  for (int ix = 0; (ix < stringArray1.Length) && areTheSame; ix++)
  {
    areTheSame = stringArray1[ix].Equals(stringArray2[ix], StringComparison.CurrentCulture);
  }
  return areTheSame;
}

June 29, 2017

Case Insensitive Dictionary


Very easy to implement in C#, just pass 'StringComparer.OrdinalIgnoreCase' in the constructor, for example:
Dictionary nameToIdTable = new Dictionary(StringComparer.OrdinalIgnoreCase);
Here is an an example in action:
[ Test ]
public void CaseInsenstiveDictionaryTest()
{
  var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase)
  {
    { "Mbukuravi", 1 },
    { "Liaedin", 2 },
  };

  Assert.That( dictionary[ "Liaedin" ] == 2 );
  Assert.That( dictionary[ "LiAEdin" ] == 2 );
  Assert.That( dictionary[ "LiaeDIn" ] == 2 );
  Assert.That( dictionary[ "LIAEDIN" ] == 2 );
}

June 14, 2017

A ConcurrentStreamWriter Class

A class for writing to a stream from multiple threads:

public interface IConcurrentStreamWriter
{
    void WriteStream(
        string theStringToWrite);
}

/// <summary>
///     A concurrent stream writer. Allows concurrent/queued writes to a stream
/// </summary>
///
/// <seealso cref="T:ASM.DEK.Printer.Micron.PrintEngine.Timing.Timers.IConcurrentStreamWriter"/>
/// <seealso cref="T:System.IDisposable"/>
public class ConcurrentStreamWriter : IConcurrentStreamWriter, IDisposable
{
    private readonly Func<StreamWriter> _streamWriterProvider;
    private readonly ConcurrentQueue<string> queuedWrites = new ConcurrentQueue<string>();
    private readonly AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

    private bool _exit;

    public ConcurrentStreamWriter(
        Func<StreamWriter> streamWriterProvider)
    {
        Contract.Requires<ArgumentNullException>(
            streamWriterProvider != null);

        _streamWriterProvider = streamWriterProvider;

        Task.Factory.StartNew(
            WriteQueueThreadRoutine, 
            TaskCreationOptions.LongRunning);
    }

    public void WriteStream(
        string theStringToWrite)
    {
        if (!string.IsNullOrEmpty(theStringToWrite))
        {
            queuedWrites.Enqueue(theStringToWrite);
            _autoResetEvent.Set();
        }
    }

    #region private

    private void WriteQueueThreadRoutine()
    {
        using (var stream = _streamWriterProvider())
        {
            while (!_exit)
            {
                _autoResetEvent.WaitOne();
                if (queuedWrites.Count > 0)
                {
                    WriteQueueImpl(stream);
                }
            }
        }
        _autoResetEvent.Dispose();
    }

    private void WriteQueueImpl(
        StreamWriter stream)
    {
        try
        {
            string stringToWrite = "";
            do
            {
                if (queuedWrites.TryDequeue(out stringToWrite))
                {
                    stream.WriteLine(stringToWrite);
                }
            } while (queuedWrites.Count > 0);
            stream.Flush();
        }
        catch (IOException ioex)
        {
            Trace.WriteLine("IOException caught: " + ioex);
        }
    }

    #endregion private

    #region Dispose Implementation

    // Use C# destructor syntax for finalization code.
    ~ConcurrentStreamWriter()
    {
        Dispose(false);
        Debug.Assert(false, "Detected ConcurrentStreamWriter object was undisposed");
    }

    private bool _isDisposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(
        bool disposing)
    {
        if (_isDisposed)
            return;

        if (disposing)
        {
            _exit = true;
            _autoResetEvent.Set();
        }

        _isDisposed = true;
    }

    #endregion Dispose Implementation
}
It uses a ConcurrentQueue object to queue the stream requests up and an AutoResetEvent to release a writer on a separate thread to actually output the stream contents.

May 25, 2017

Fluent Builder Template

Fluent interfaces Create a more simplified language style approach to coding.
Here is a template for an object builder class that gives a fluent interface for the construction of objects:
[ ExcludeFromCodeCoverage ]
public class $XXX$Builder
{
    private $IYYY$ _mock$IYYY$ = Substitute.For<$IYYY$>();

    public $XXX$Builder With$IYYY$(
        $IYYY$ param )
    {
        _mock$IYYY$ = param;
        return this;
    }

    public static implicit operator $XXX$( $XXX$Builder builder )
    {
        return builder.Build();
    }

    public $XXX$ Build()
    {
        return new $XXX$( _mock$IYYY$ );
    }
}
Replace $XXX$ with your concrete builder target type
For each service replace $IYYY$ with the service name

2 Dimensional List Sort Using Linq

A 2 dimensional list sort
[ Test ]
public void List2DSortTest()
{
    var table = new List<List<string>>
    {
        new List<string> { "1", "Mbukuravi", "2", "Wnuk-Lipinski Settlement", "M2M" },
        new List<string> { "2", "Liaedin", "Ulrich's Rock", "Valigursky Landing", "L1M" },
        new List<string> { "3", "Miao Thixo", "2 A", "Sekelj Laboratory", "M3M" },
    };

    var newTable = table.OrderBy( list => list[ 1 ] ).ToList();
    Assert.That( newTable[ 0 ][ 1 ] == "Liaedin" );
    Assert.That( newTable[ 1 ][ 1 ] == "Mbukuravi" );
    Assert.That( newTable[ 2 ][ 1 ] == "Miao Thixo" );

    var newTable2 = table.OrderByDescending( list => list[ 1 ] ).ToList();
    Assert.That( newTable2[ 2 ][ 1 ] == "Liaedin" );
    Assert.That( newTable2[ 1 ][ 1 ] == "Mbukuravi" );
    Assert.That( newTable2[ 0 ][ 1 ] == "Miao Thixo" );

    var newTable3 = table.OrderBy( list => list[ 2 ] ).ToList();
    var newTable4 = table.OrderByDescending( list => list[ 2 ] ).ToList();
}

March 2, 2017

An Exception Handling String Formatter

A string extension class to safely use string.Format().
public static class StringExtensions
{
    // Safe format, capture any Format Exceptions.
    public static string TryFormat(
        this string formatText,
        params object[] parameters)
    {
        string res = "";
        try
        {
            res = string.Format(formatText, parameters);
        }
        catch (FormatException fe)
        {
            string msg = "Formatting string error : formatText=" + formatText + 
                                ", parameters=\'" +
                              string.Join( ",", parameters ) + "\', exception=" + fe;
            Trace.WriteLine("string.Format code needs fixing - " msg);
            res = formatText;
            Debug.Assert(false, msg);
        }
        return res;
    }
}
I have seen this method take down a complete application because too few parameters were passed.

Thread Safe Enum Access Using Interlocked

Here is an example class that makes reading and writing an enum property thread safe (when writing or reading the enum value)
/// <summary>
///     A thread safe MyEnum.
/// </summary>
public class ThreadSafeMyEnum
{
 // Volatile is not needed (esp. on x64 or x86) : 
 // http://stackoverflow.com/questions/7177169/how-to-apply-interlocked-exchange-for-enum-types-in-c 
 // http://stackoverflow.com/questions/425132/a-reference-to-a-volatile-field-will-not-be-treated-as-volatile-implications
 private int _enumAsInt;

 public MyEnum Value // added for convenience
 {
  get { return ( MyEnum ) Interlocked.CompareExchange( ref _enumAsInt, 0, 0 ); }
  set { Interlocked.Exchange( ref _enumAsInt, ( int ) value ); }
 }
}

NUnit Parameterised Tests

NUnit can have class level parameterised tests:


[ TestFixture( MachineEnum.None ) ]
[ TestFixture( MachineEnum.Lathe ) ]
[ TestFixture( MachineEnum.Miller ) ]
public class SomeTests
{
  private readonly MachineEnum _machineEnum = MachineEnum.None;

  public SomeTests(
  MachineEnum machineEnum )
  {
    _machineEnum = machineEnum;
  }
  ...
}

Also the tests can be paramterised at the test level. Make sure the TestCase parameter match the order and type of the test method.

  [Test]
  [TestCase("some value", null,         ExpectedResultEnum.Paramater1Used)]
  [TestCase("some value", "over ride",  ExpectedResultEnum.Paramater2Used)]
  [TestCase("some value", "over ride",  ExpectedResultEnum.Paramater1Used)]
  [TestCase(null,         null,         ExpectedResultEnum.Paramater1Used)]
  public async Task ParamterisedTest(
    string param1,
    string param2,
    ExpectedResultEnum expectedResult)
{
    // Arrange

    // Act

    // Assert
    Assert.Multiple(() =>
    {
      Assert.That(var1 == null, Is.False);
      Assert.That(var2.Id, Is.EqualTo(expectedResult));
    });
    ...
}

February 20, 2017

Double Equals Extension Class

Doubles cannot be compared without specifying an acceptable error
//     A double extensions class.
public static class DoubleExtensions
{ 
 public static bool IsEqualTo(
  this double val1,
  double val2,
  double resolution ) // Resolution of the double comparison, specifies an acceptable error level
 {
  double compare = Math.Abs( val1 - val2 );
  bool res = compare < resolution;
  return res;
 }
}

Comand Line Parser Class

A special command line parser class that automatically parses command lines specified in a particular way
    // Parse command line arguments in the form
    // /xxx
    // -xxx
    // OR
    // /xxx:yyy
    // -xxx:yyy
    // where xxx is a parameter name and yyy, when present, is a value to associate with it
    // The actual command line argument is case insensitive (but not the value)
    // so /username:Bob is the same as /USERNAME:Bob
    public class CommandLineParser
    {
        readonly Dictionary<string, string> arguments = new Dictionary<string, string>();

        public void ParseCommandLineArguments(
            params string[] args )
        {
            const int NOTFOUND = -1;
            //string[] args = Environment.GetCommandLineArgs();
            Debug.WriteLine( "Args:" + string.Join( ",", args ) );
            int ix = 0;
            foreach ( string arg in args )
            {
                Debug.WriteLine( "arg[" + ix++.ToString() + "]=\'" + arg + "\'" );
            }
            foreach ( string rawArg in args )
            {
                // Get rid of whitespace chars at the beginning and end
                string arg = rawArg.Trim();
                bool argFound = ( arg.Length >= 2 ) && ( arg[ 0 ] == '/' || arg[ 0 ] == '-' );
                if ( !argFound )
                    continue;
                string argument = arg.Substring( 1 );
                int end = argument.IndexOf( ':' );
                string paramName = ( end > -1 ) ? argument.Substring( 0, end ) : argument;
                if ( ( end == NOTFOUND ) && ( paramName.Length > 0 ) )
                {
                    arguments.Add( paramName.ToUpperInvariant(), "" );
                }
                else
                {
                    string value = argument.Substring( end + 1 );
                    arguments.Add( paramName.ToUpperInvariant(), value );
                }
            }
            Debug.WriteLine( "Processed command line args:" );
            foreach ( string argName in arguments.Keys )
            {
                Debug.WriteLine( argName + " = " + arguments[ argName ] );
            }
        }

        public bool GetArgument(
            string arg,
            out string value )
        {
            value = "";
            return arguments.TryGetValue( arg.ToUpperInvariant(), out value );
        }

        // eg '/u' could select the argument /username:Jim as long as no other arguments starts with 'u'
        public bool GetUniqueArgumentStartingWith(
            string startsWith,
            out string value )
        {
            value = "";
            bool res = string.IsNullOrEmpty( startsWith.Trim() );

            if ( !res )
            {
                var keys = arguments.Keys.Where( ky => ky.StartsWith( startsWith.ToUpperInvariant() ) ).ToList();
                res = ( keys.Count == 1 );
                if ( res )
                {
                    res = arguments.TryGetValue( keys[ 0 ], out value );
                }
            }
            return res;
        }

        public bool HasUniqueArgumentStartingWith(
            string startsWith )
        {
            bool res = !string.IsNullOrEmpty( startsWith.Trim() );

            if ( res )
            {
                var keys = arguments.Keys.Where( ky => ky.StartsWith( startsWith.ToUpperInvariant() ) );
                res = ( keys.Count() == 1 );
            }
            return res;
        }

        public bool HasArgument(
            string arg )
        {
            return arguments.ContainsKey( arg.ToUpperInvariant() );
        }

        public IEnumerable<KeyValuePair<string, string>> Arguments()
        {
            foreach ( var entry in arguments )
            {
                yield return entry;
            }
        }
    }
Unit testss that accomapny and demonstrate the use of the class:
[ TestFixture ]
public class CommandLineParserUnitTests
{
    [ Test ]
    public void SeveralValidArgsTest()
    {
        CommandLineParser clp = new CommandLineParser();
        clp.ParseCommandLineArguments( "/user:John", "-password:All0aJadgar", "-IncludeMetaData" );

        Assert.That( clp.HasArgument( "IncludeMetaData" ) );
        string value;
        bool res = clp.GetArgument( "user", out value );
        Assert.IsTrue( res );
        Assert.That( value == "John" );
        res = clp.GetArgument( "smugering", out value );
        Assert.IsFalse( res );
    }

    [ Test ]
    public void EmptyArgAndValueTest()
    {
        CommandLineParser clp = new CommandLineParser();
        clp.ParseCommandLineArguments( "/:" );

        Assert.That( !clp.HasArgument( ":" ) );
    }

    [ Test ]
    public void NoValueSpecifiedTest()
    {
        CommandLineParser clp = new CommandLineParser();
        clp.ParseCommandLineArguments( "/dufftest:" );
        string value;

        Assert.That( clp.GetArgument( "dufftest", out value ) );
        Assert.That( value.Length == 0 );
        Assert.That( clp.HasArgument( "dufftest" ) );
    }
}