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.
public interface ICliAttribute
{
bool Option { get; }
bool BuiltIn { get; }
bool Required { get; set; }
FlagTypes FlagTypes { get; set; }
ValueTypes ValueTypes { get; }
object[] Args { get; set; }
string Name { get; set; }
string Description { get; set; }
string[] Flags { get; set; }
object[] Values { get; set; }
object Default { get; set; }
MemberInfo MemberInfo { get; set; }
Type MemberInfoType { get; }
bool IsAssigned(object target);
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
.
public static class CliBuilder
{
#region props
public static bool ExitOnError { get; set; }
public static string Filename { get { return Path.GetFileName(
System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); } }
public static string XmlFilename { get { return Path.ChangeExtension(Filename, ".xml"); } }
public static string IniFilename { get { return Path.ChangeExtension(Filename, ".ini"); } }
public static string CliFilename { get { return Path.ChangeExtension(Filename, ".cli"); } }
public static string Title { get; set; }
public static string Description { get; set; }
public static FlagTypes Flags { get; internal set; }
public static string[] Args { get; internal set; }
public static string[] RemainingArgs { get; internal set; }
public static Generators Generators { get; internal set; }
public static Func<string, string, ICliAttribute[], string> Usage { get; set; }
[CliAttribute(Description = "show this help message and exit")]
public static bool Help { get; set; }
[CliAttribute(Description = "return the version of the program")]
public static bool Version { get; set; }
[CliAttribute(Description = "return the version of the Cli assembly")]
public static bool CliVersion { get; set; }
[CliAttribute(Description = "save the parameters as an xml file")]
public static string SaveConfig { get; set; }
[CliElement(Description = "list of cli config files to load")]
public static string[] 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
using Idlesoft.Cli;
2. Define a class
public class Person
{
[CliAttribute(Description = "your name?")]
public string Name { get; set; }
[CliAttribute(Description = "your age?")]
public int Age { get; set; }
[CliElement(Description = "list of pets?")]
public string[] Pets { get; set; }
}
3. Call CliBuilder.Create<TTarget>
var person = CliBuilder.Create<Person>(PERSON_TITLE, PERSON_DESC,
FlagTypes.Default, PERSON_ARGS);
4. Use the class object
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.