Declarative Command-Line Argument Parsing
In .NET, there is a wonderful feature - custom attributes. They make it possible to introduce a new level of abstraction: a declarative one. At this level, dependencies on specific implementations are not important; there is no need to inherit from classes or implement interfaces. The programmer writes in code what they want to obtain, without caring about the details of how this “what” will be implemented or executed.
There are many examples of attribute usage in any .NET application. For instance, attributes are used to describe how classes are mapped to tables in LINQ to SQL. Attributes are also used to implement C# language features that are not directly supported by the .NET CLR, such as extension methods and implicit parameters (parameters with default values).
I faced the task of parsing command-line arguments in at least two applications. Since writing pages full of if statements and string comparisons is not particularly exciting, I decided to take a declarative approach and use custom .NET attributes. The concept of custom attributes fits command-line argument parsing very well, because attributes can be applied to object properties into which parameter values will be written.
As a result, I ended up with a command-line argument parser that can not only extract parameter values, but also execute actions.
For the parser, an object is created that contains parameter properties and action methods.
internal class OptionSet
{
[DefaultOption]
[Option(Name = "/help", Aliases = "/?|?|help|--help|-h|/h|-help")]
public void Help()
{
Trace.WriteLine("Options parser test utility");
Trace.WriteLine("--------------------------------------------");
Trace.WriteLine("Please specify one of the following options:\n"
+ "/help - to show this help message;\n"
+ "/install - to install the service;\n"
+ "/uninstall - to uninstall the service;\n");
}
[Option(Name = "/install")]
public void Install()
{
Trace.WriteLine("Installing");
...
}
[Option(Name = "/uninstall")]
public void Uninstall()
{
Trace.WriteLine("Uninstalling");
...
}
[Option(Name = "-dr=")]
public DbDriver Driver
{
get;
set;
}
[Option(Name = "-s=")]
public string Server
{
get;
set;
}
[Option(Name = "-db=")]
public string Schema
{
get;
set;
}
[Option(Name = "-k=")]
public int ObjectKey
{
get;
set;
}
[Option(Tail = true)]
public string ObjectParams
{
get;
set;
}
}
The parser supports the following parameter types: string, number, enumerations, and a boolean type where only the presence of a particular argument matters.
The parsing itself is performed as follows.
static int Main(string[] args)
{
OptionSet optionSet = new OptionSet();
OptionParser parser = new OptionParser(optionSet, OptionParserParameters.MustHaveActions);
try
{
parser.Parse(args);
return 0;
}
catch (OptionException exception)
{
Trace.WriteLine("Error: " + exception.Message);
Trace.WriteLine("Use /help parameter to view command list");
return 1;
}
catch (OptionExecutionException exception)
{
Trace.WriteLine("Error: " + exception.Message);
return 2;
}
}
Parser Options
[Flags]
public enum OptionParserParameters
{
None = 0,
KeyCaseSensitive = 1,
ValueCaseSensitive = 2,
ValueInSeparatedArgument = 4,
MustHaveActions = 8,
AllowMultipleActions = 16
}




