Introduction

You often need to create a console application that takes parameters to execute different functions.
It is not uncommon to start with positional parameters until you realize it would be better if you could parse the commandline args for flags.
When you get to this point, you have two options. You can roll your own, or use something that has been written for this purpose.
I am suggesting you use my CLI tool for C# apps from now on and save yourself the time it takes to roll your own each time.
I am developing this as an Open-Source project at cli.codeplex.com.

Background: Reflection, Attributes, and XML Serialization

I use two ideas heavily in the implementation of CLI.

Reflection

If you are not familiar with C# Reflection, rest assured it is a big subject.
However, I think it can be best summarized as the ability to programmatically find out the names of variables instead of just their values. Another common thing
is to programmatically find out members of a class where members can be fields, properties, or methods irrespective of their protection level (public, protected, private, internal).
This is something that usually only the programmer knows. There is usually a fairly high penalty for using Reflection. However, if you selectively use it, it can be quite powerful.

Attributes

Attributes are somewhat a subset of the Reflection discussion. In so much as you can reference custom attributes via Reflection as I do in CLI.
More information can be found here. Attributes give you the ability
to decorate or mark up your code and gain extra functionality. Serialization is a common example.

XML Serialization

XML Serialization is a well trodden topic and many articles can be found covering the subject. I will summarize here by saying that it is the ability to take an object
in memory and save it to desk in the form of an XML file. The reverse is also possible by instantiating an object from an XML file representing it.

Using the CLI Library

Code Overview

Here is a brief explanation of the most important classes in the CLI library. This is how the user will interface with the features offered by automatic commandline parsing and assignment.
Please take a moment to look through the interfaces listed below to get a feel for what is possible and how it works.

ICliAttribute interface

Both custom attributes CliAttribute and CliElement implement this interface and in fact the only reason to use
CliElement is for XML serialization considerations, not covered here.

publicinterface ICliAttribute
{
///<summary>/// Boolean property to determine if the cliattribute
/// is an Option as opposed to a BuiltIn or Required.
///</summary>bool Option { get; }
///<summary>/// Boolean property to determin if the cliattribute
/// is decorating the CliBuilder class instead of a user-defined one.
///</summary>bool BuiltIn { get; }
///<summary>/// Optional boolean property to mark a cliattribute as required.
/// This will cause an error to happen and the usage
/// to print if it is not set during CliBuilder.Create.
///</summary>bool Required { get; set; }
///<summary>/// Optional property to override the default flag types as defined
/// during the call to CliBuilder.Create or its default value.
/// This governs how and how many of the flags are auto-generated.
///</summary> FlagTypes FlagTypes { get; set; }
///<summary>/// Internal property that determines what kind of values
/// are appropriate for the ICliAttribute decorated field or property.
///</summary> ValueTypes ValueTypes { get; }
///<summary>/// Internal property used to arg matches including the name and the values.
///</summary>object[] Args { get; set; }
///<summary>/// Optional property to override the name of the property.
/// This overridden Name is then used when auto-generating the flags to match against.
///</summary>string Name { get; set; }
///<summary>/// Optional property used when the help usage prints
/// to the console on bad input are when invoked.
///</summary>string Description { get; set; }
///<summary>/// Optional string array of flags to be used to match against.
/// This turns off the auto-generated flags irrespectives of the FlagTypes etc.
///</summary>string[] Flags { get; set; }
///<summary>/// Internal property used to store the values once they have been
/// parsed out of the command line args. Do not attempt to set this property.
///</summary>object[] Values { get; set; }
///<summary>/// Optional property to supply default values to be used when no values
/// are found during parsing. Note that this will not be used
/// for cliattributes marked as Required.
///</summary>object Default { get; set; }
///<summary>/// Internal property to store the MemberInfo loaded during
/// the CliBuilder.Create execution. Do not attempt to set this property.
///</summary> MemberInfo MemberInfo { get; set; }
///<summary>/// Internal property to get the type of the property|field
/// that the MemberInfo property points to.
/// Will throw an exception if MemberInfo is not set.
///</summary> Type MemberInfoType { get; }
///<summary>/// Internal method to determine if a cliattribute has been assigned.
/// Checks against the default values; this will probably be improved to be more accurate.
///</summary>///<paramname="target">The target object that
/// the MethodInfo property is point to.</param>///<returns></returns>bool IsAssigned(object target);
///<summary>/// Internal method to determin if either the Name
/// or any of the Values match the given arg.
///</summary>///<paramname="arg"></param>///<returns></returns>bool MatchesArg(string arg);
}

CilBuilder public properties

CliBuilder is the static class that instantiates your CliAttribute-decorate class. It provides some defaults and abilities to override them.
It does all the work to match and assign command line args to your class properties. It also, itself, has "built-in" CliAttribute-decorated properties
such as Help and Version.

publicstaticclass CliBuilder
{
#region props
publicstaticbool ExitOnError { get; set; }
///<summary>/// Produces the name of current process' main module filename
///</summary>publicstaticstring Filename { get { return Path.GetFileName(
System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); } }
///<summary>/// Produces the Filename with the .xml extension instead
///</summary>publicstaticstring XmlFilename { get { return Path.ChangeExtension(Filename, ".xml"); } }
///<summary>/// Produces the Filename with the .ini extension instead
///</summary>publicstaticstring IniFilename { get { return Path.ChangeExtension(Filename, ".ini"); } }
///<summary>/// Produces the Filename with the .cli extension instead
///</summary>publicstaticstring CliFilename { get { return Path.ChangeExtension(Filename, ".cli"); } }
///<summary>/// The title to be used in help usage printing
///</summary>publicstaticstring Title { get; set; }
///<summary>/// The decsription to be used in help usage printing
///</summary>publicstaticstring Description { get; set; }
///<summary>/// The default flags value to be used on CliAttributes that don't otherwise specify
///</summary>publicstatic FlagTypes Flags { get; internalset; }
///<summary>/// The args to be used in parsing, either passed in with
/// the Create method or assumed to be Environment.GetCommandLineArgs()
///</summary>publicstaticstring[] Args { get; internalset; }
///<summary>/// Is set to the remaining args that were not found
/// in the class when instantiating an object via CliBuilder.Create
///</summary>publicstaticstring[] RemainingArgs { get; internalset; }
///<summary>/// Dictionary of flag values and expression generators to be
/// used for automatically divining flag tokens to match against in parsing
///</summary>publicstatic Generators Generators { get; internalset; }
///<summary>/// Method to be used when 'help' is invoked; can be overriden
/// by replacing this function with your own printing function
///</summary>publicstatic Func<string, string, ICliAttribute[], string> Usage { get; set; }
///<summary>/// Built-In property to match against Help and print the usage text.
/// Will default to the common -h|--help unless other flags specified override this behavior
///</summary> [CliAttribute(Description = "show this help message and exit")]
publicstaticbool Help { get; set; }
///<summary>/// Built-In property to match against Version and print the version text.
/// Will default to the common -v|--version unless other flags specified override this behavior
///</summary> [CliAttribute(Description = "return the version of the program")]
publicstaticbool Version { get; set; }
///<summary>/// Built-In property to match against CliVersion and print the version
/// of cli text. Will default to the common -c|--cliversion unless
/// other flags specified override this behavior
///</summary> [CliAttribute(Description = "return the version of the Cli assembly")]
publicstaticbool CliVersion { get; set; }
///<summary>/// Built-In property to match against SaveSonfig which is a path, filename
/// and extension that specifies where, with what name and what type
/// of config file to save from the parameters that are loaded during Create
///</summary> [CliAttribute(Description = "save the parameters as an xml file")]
publicstaticstring SaveConfig { get; set; }
///<summary>/// Built-In property to match against LoadConfigs which is an array
/// of paths to configs files (.xml, .ini, .cli) that will load values
/// PRIOR to the other commandline options that may be included
///</summary> [CliElement(Description = "list of cli config files to load")]
publicstaticstring[] LoadConfigs { get; set; }
#endregion props
}

Simple Usage Example

Now I will show a simple straightforward example of how to use the CLI library. It should only take seconds to enable an application to have a quick, concise,
coherent interface complete with --help functionality.

1. Add reference

// 1) Add reference to the cli library and include the namespace
using Idlesoft.Cli;

3. Call CliBuilder.Create<TTarget>

// 3) Make call to CliBuilder.Create early on in your code
// to parse commandline args and populate your decorated class object
var person = CliBuilder.Create<Person>(PERSON_TITLE, PERSON_DESC,
FlagTypes.Default, PERSON_ARGS);
// try replacing PERSON_ARGS with args or removing it all together

4. Use the class object

// 4) Use the class object with populated properties
Console.WriteLine(string.Format("My name is {0} and I am {1} years old. I have {2} pets: {3}.",
person.Name, person.Age, count, pets));

Points of Interest

I really like using the impressive flexibility of the C# programming language to make my life easier.
Reflection is a powerful, if expensive tool, that can be used to really extend the functionality of any program written in C#.
I am working on this as an Open Source project at cli.codeplex.com. Any feedback, ideas, suggestions, bug fixes are always welcome.

Remaining Work to be Done

I still need to implement and test some of the XML serializaion features I want to enable. I would also like to cleanup the help print screen.
I have been using the Python library argparse at work lately and really like working with it. Maybe I will borrow some of the ideas there.
I do like C#'s ability to decorate properties in a more declarative way, so I wouldn't be changing that. ;)

History

Initial submission.

License

This article, along with any associated source code and files, is licensed under The MIT License

Very interesting. It seems we have some similar concepts. I am not sure I like how you put everything on methods rather than properties. I am not sure that will scale well with a lot of args. I do like your validation attributes like LessThan etc. I also like your coding style and completeness with documentation. Cheers!