Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Flexible C# Command Line Parsing via Attributes

0.00/5 (No votes)
4 Apr 2018 2  
A flexible command line parsing class that uses C# attributes and reflection to discover syntax

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here