Introduction
Sometimes, it's still useful to drop back to the command line, especially when an application may be invoked via a script. The classes in this library provide a means of easily obtaining options from the command line, without worrying about the complexity of parsing that command line.
To use these classes, simply create a C# class with public
properties for each option the user can specify and decorate each property with an attribute that describes its command line syntax.
The class library will then discover the syntax via reflection, handle any necessary type conversions, and enforce any rules about required or excluded options.
The included demo requires Visual Studio 2017 (or later) to build. The solution was originally built using Visual Studio Professional and includes a unit test project. If the unit test project causes issues, for your edition of Visual Studio, it can be safely removed.
Background
This is a common problem and there are many other packages out there that provide similar (or greater) capabilities. This particular library is designed to be lightweight, flexible, well-tested, and meets my specific requirements. I am sharing it with the community in case others have similar requirements.
Using the Code
From a consumer's perspective, these are only the three simple classes in this library:
Class Name | Description |
CommandLineClassAttribute | Use this attribute to decorate option classes to provide the location of optional help resources. |
CommandLinePropertyAttribute | Use this attribute to decorate properties, within option classes, and provide rules for the syntax of an option when presented on the command line. |
CommandLineParser | This class provides a parser for a specified option class decorated with the attributes described above. |
A simple example of an option class is provided below:
using Transeric.CommandLine;
using CommandLineParserDemo.Properties;
namespace CommandLineParserDemo
{
[CommandLineClass(typeof(Resources))]
public class SimpleOptions
{
[CommandLineProperty("optionA")]
public string OptionA { get; set; }
[CommandLineProperty("optionB", excludes:"optionA")]
public int OptionB { get; set; }
}
}
This class describes a syntax where the user can specify either /optionA:ABC
or /optionB:123
on a command line, but not both. The parser class automatically handles data type conversions and enforcement of the exclusion rule.
A program that uses this option class, can be as trivial as follows:
using System;
using Transeric.CommandLine;
namespace CommandLineParserDemo
{
public class Program
{
public static void Main(string[] args)
{
var options = new SimpleOptions();
var parser = new CommandLineParser(options);
if (parser.Parse(args))
{
parser.WriteHelp(Console.Out);
return;
}
Console.WriteLine(options.OptionA);
Console.WriteLine(options.OptionB);
}
}
}
With this, any of the following formats would be valid for a command entered by the user:
.\CommandLineParserDemo.exe /optionA:ABC
.\CommandLineParserDemo.exe /optionA=ABC
.\CommandLineParserDemo.exe /optionA ABC
.\CommandLineParserDemo.exe /optionB:123
.\CommandLineParserDemo.exe /optionB=123
.\CommandLineParserDemo.exe /optionB 123
The following formats would not be valid. In the first case, the value "ABC
" is not valid for the integer property SimpleOptions.OptionB
. In the second case, the excludes
parameter associated with "optionB
" prevents "optionA
" from also being specified.
.\CommandLineParserDemo.exe /optionB:ABC
.\CommandLineParserDemo.exe /optionA=ABC /optionB:123
While not obvious in the examples above, the parser will also automatically recognize abbreviations. The user need only provide sufficient characters, on the command line, so that the option name is unambiguous. For example, if the syntax had two different options, /alpha
and /beta
, the user need only specify /a
and /b
.
The command line arguments may not be as readily available for WinForm and WPF applications. The easiest way to get them is via the Environment.GetCommandLineArguments
method or Environment.CommandLine
property.
The remainder of this article provides technical detail that may exceed the needs of the average user. It may be quicker to simply play with the sample application provided with this article. The sample application is divided into three projects:
Project Name | Description |
CommandLineParserDemo | A trivial application that demonstrates this class library by displaying requested file names. |
Transeric.CommandLine | All of the code necessary to parse command lines. This is the only class library that needs to be included in your own applications. |
Transeric.CommandLine.Tests | All of the unit tests for the Transeric.CommandLine project. This is not necessary for your own applications. |
CommandLineClassAttribute Class
As mentioned earlier, the CommandLineClassAttribute
is used to decorate an options class to provide the location of help resources. Its parameters are as follows:
Parameter Name | Description |
resourceType | This provides the type of a resource class within your project. These classes are easily added to a project, in Visual Studio, by using Add > New Item... > Resources File. |
helpKey | This provides the optional name of a string resource containing help text. If omitted, a default value is assumed. The default help key is generated from the class name, suffixed by the text "_Help ". So, for example, the default help key for the class MyOptions would be "MyOptions_Help ". |
CommandLinePropertyAttribute Class
As mentioned earlier, the CommandLinePropertyAttribute
is used to decorate properties, within an option class, to provide the command line syntax of the option and to provide the location of help resources. Its parameters are as follows:
Parameter Name | Description |
name | The required name of the option as it appears on the command line. |
helpKey | This provides the optional name of a string resource containing help text. If omitted, a default value is assumed. The default help key is generated from the property name, suffixed by the text "_Help ". So, for example, the default help key for the property MyProperty would be "MyProperty_Help ". |
requires | The provides an optional comma-separated list of option names that must also be present when this option is specified. |
excludes | This provides an optional comma-separated list of option names that are prohibited when this option is specified. |
position | While all options can be specified by name (e.g. command /optionA:ABC ), some options can also be specified positionally (e.g. command ABC). This parameter provides the zero-based index of the such options. In the provided example, position would be zero (the command is not counted). An exception will occur if the position would create gaps or duplication in the sequence of positions. |
isRequired | This provides a boolean value indicating if the option is always required. |
listSeparatorChar | Some options may be associated with properties that have a list or array data type. This parameter provides the character used to separate individual items, within the list, on the command line. The default value is comma (','). So, for example, the following option might be valid: /list:option-1,option-2,option-3 . |
Exception Handling
There are a large number of exceptions that can be thrown by this class library. However, almost all of them are derived from one of three classes:
Class Name | Description |
CommandLineClassException | These exceptions may be thrown, from the constructor for CommandLineParser , to indicate a problem with the CommandLineClassAttribute attached to an options class. |
CommandLinePropertyException | These exceptions may be thrown, from the constructor for CommandLineParser , to indicate a problem with the CommandLinePropertyAttribute attached to a property within an options class. |
CommandLineParseException | These exceptions may be thrown, by the CommandLineParser.Parse method, to indicate a syntax error or data type conversion error. |
The full set of exceptions derived from CommandLinePropertyException
are as follows:
Class Name | Description |
BooleanRequiredOrPositionalException | A boolean property specifies either isRequired or position . Since boolean options are either present or absent, and have no associated value, the requested behavior is not possible. |
DuplicatePropertyException | Multiple properties specify the same name . |
InvalidOptionNameException | A property specifies an invalid option name . Option names must be at least one character, start with a letter, and consist entirely of letters and digits. |
IsRequiredExcludesException | A property specifies both isRequired and excludes . This would effectively always exclude the specified propert(ies). |
NoPropertySetterException | A property lacks a public setter method. |
PropertyPositionException | A property specifies a position that would either create gaps in the sequence of zero-based positions or duplicate another position within that sequence. |
PropertyReferenceException | A property specifies excludes or requires , where the option name identifies an undefined option, is specified multiple times, references itself, or is present in both excludes and requires |
The full set of exceptions derived from CommandLineParseException
are as follows:
Class Name | Description |
CommandLineOptionException | The base class for all CommandLineParseException exceptions that are associated with a specific option. |
ConflictOptionException | An excluded option was provided along with the option that excluded it. |
DuplicateOptionException | The same option was specified multiple times. |
InvalidCommandLineArgumentException | An unrecognized option or unexpected value was specified. |
InvalidOptionValueException | An invalid value was specified for an option (e.g., a text value was provided for an integral property). |
MissingOptionValueException | A non-boolean option was missing its value. |
MissingRequiredOptionException | A required option was missing. |
Points of Interest
When a value is specified as a separate argument, it cannot begin with the slash character (or CommandLineParser.OptionIndicator
). By default, when the arguments are split by Windows, quotes are removed. So, it is not possible to easily distinguish between "/option
" and /option
. To do so, would require parsing the raw command line.
History
- 3/27/2018 - The original version was uploaded.
- 3/27/2018 - Minor typo fixes and cosmetic changes.