A little while ago I was fiddling with dependency-injection and came up with this small WPF application that I can launch with command-line arguments.Not only this was my first experiment with DI, it was also my first experiment with parsing command-line arguments. The result works nicely, but looking at the code today I see a number of things I would have done differently.What would you have done differently?Looking mainly for feedback on readability and maintainability.I have an interface/class that exposes getters for all possible command-line arguments; the implementation's constructor uses Ninject's InjectAttribute to specify the required ICommandLineArg implementation to receive:public class AutoBuildCommandLineHelper : IAutoBuildCommandLineArgs{    public ICommandLineArg QuietInterfaceArgument { get; private set; }    public ICommandLineArg BuildServerArgument { get; private set; }    public ICommandLineArg CompletionMessageArgument { get; private set; }    public ICommandLineArg LoggingDisabledArgument { get; private set; }    public ICommandLineArg FailureNotificationOnlyArgument { get; private set; }    public AutoBuildCommandLineHelper([QuietInterfaceArg] ICommandLineArg quietInterfaceArg,                                       [BuildServerArg] ICommandLineArg buildServerArg,                                       [NoLogArg] ICommandLineArg noLogArg,                                       [MessageArg] ICommandLineArg msgArg,                                       [FailureNotifyArg] ICommandLineArg failArg)    {        QuietInterfaceArgument = quietInterfaceArg;        BuildServerArgument = buildServerArg;        LoggingDisabledArgument = noLogArg;        CompletionMessageArgument = msgArg;        FailureNotificationOnlyArgument = failArg;    }}Here are a couple ICommandLineArg implementations:public class QuietInterfaceArgument : CommandLineArgumentBase{    public QuietInterfaceArgument(string[] args)        : base(args, q, quiet) { }}public class NoLoggingArgument : CommandLineArgumentBase{    public NoLoggingArgument(string[] args)        : base(args, n, nolog) { }}As you see there's really nothing going on here - this is the base class:public abstract class CommandLineArgumentBase : ICommandLineArg{    private readonly string[] _args;    private const char ParamSpecifier = ':';    private readonly char[] _switches = { '-', '/' };    public string Name { get; private set; }    public string Alias { get; private set; }    public bool MustHaveParameter { get; private set; }    protected CommandLineArgumentBase(string[] args, string name, string alias)         : this(args, name, alias, false) { }    protected CommandLineArgumentBase(string[] args, string name, string alias, bool mustHaveParam)    {        _args = args;        Name = name;        Alias = alias;        MustHaveParameter = mustHaveParam;    }    public virtual string ParameterValue()    {        if (!IsSpecified()) throw new InvalidOperationException();        var arg = GetArgument();        var values = arg.Split(ParamSpecifier);        if (MustHaveParameter && values.Length == 1) throw new InvalidOperationException();        return values[1].TrimStart('').TrimEnd('');    }    private string GetArgument()    {        if (!IsSpecified()) throw new InvalidOperationException();        return _args.Single(arg => new[] {Name, Alias}.Contains(TrimArgument(arg).Split(ParamSpecifier)[0]));    }    private string TrimArgument(string arg)    {        return arg.TrimStart(_switches).TrimEnd(_switches);    }    public bool IsSpecified()    {        return _args.Select(arg => arg.Split(ParamSpecifier)[0])                    .Select(TrimArgument)                    .Contains(Name) || _args.Any(arg => TrimArgument(arg).StartsWith(Name));    }}This thing is used in Program.cs / Main(), like this:        if (argsHelper.LoggingDisabledArgument.IsSpecified())             logProvider.DisableLogging();        if (argsHelper.QuietInterfaceArgument.IsSpecified()          || argsHelper.BuildServerArgument.IsSpecified())        {            // running with -quiet or -build command-line switches: just execute and exit.            autoBuildApp.Execute();        }        else        {            // run with UI:            var app = new App(autoBuildApp);            app.Run();        }I like that arguments are strongly-typed and all I need to do to check if they're specified, is to call .IsSpecified() - the flipside is that to achieve this I'm passing the args string everywhere, so it gets parsed every time.Thoughts?
First attempt at dealing with command-line args
c#;strings;parsing
StyleI don't like the name AutoBuildCommandLineHelper for a class which implements an interface like IAutoBuildCommandLineArgs. I would have named it AutoBuildCommandLineArgsYou have made ParamSpecifier a named constant but not the quotes you strip of the parameter value.Possible BugsIf I read IsSpecified() correctly then it accepts arguments starting with Name. Given that Name seems to be the one letter abbreviation this means if I specify an invalid argument which happens to start with a letter of a valid argument then IsSpecified() will return true.If you call ParameterValue() on an argument which does not have (i.e. doesn't require) a parameter it will throw an IndexOutOfRangeException as values will only have 1 entryDesignWhen you want to add a new command line option you have to do 5 or 6 things:Add a new class representing the new optionRegister that class with the IoC container (this could possibly happen automagically depending on the container used)Add a new property to IAutoBuildCommandLineArgsAdd a new property to AutoBuildCommandLineHelperAdd a new property to AutoBuildCommandLineHelper constructorAdd a new assignment in AutoBuildCommandLineHelper constructor to copy parameter into propertySeems a bit involved. I hacked together an alternative version which should fit DI just nicely:public enum Option{    NoLog,    Quiet,    Server,    LogLevel,    Help,}public interface ICommandLineOptions{    bool Has(Option option);    string ValueOf(Option option);}public class CommandLineOptions : ICommandLineOptions{    private class CommandLineOption    {        public CommandLineOption(Option type, string shortKey, string longKey, string description, bool requiresParam)        {            Type = type;            Short = shortKey;            Long = longKey;            Description = description;            RequiresParam = requiresParam;         }        public readonly Option Type;        public readonly string Short;        public readonly string Long;        public readonly string Description;        public readonly bool RequiresParam;    }    private static readonly List<CommandLineOption> _AllOptions =         new List<CommandLineOption>        {            new CommandLineOption(Option.NoLog,     n, nolog,    Don't log,          false),            new CommandLineOption(Option.Quiet,     q, quiet,    Run quietly,        false),            new CommandLineOption(Option.Server,    s, server,   Run in server mode, false),            new CommandLineOption(Option.LogLevel, ll, loglevel, Set log level,      true),            new CommandLineOption(Option.Help,      ?, help,     Print help,         false),        };    private static const char _ParamSpecifier = ':';    private static readonly char[] _Switches = { '-', '/' };    private static readonly char[] _Quotes = { '', '\'' };    private Dictionary<Option, string> _AvailableOptions = new Dictionary<Option, string>();    public CommandLineOptions(string[] args)    {        foreach (var arg in args.Select(a => a.Trim(_Switches)))        {            var parts = arg.Split(new char[] {_ParamSpecifier }, 2, StringSplitOptions.RemoveEmptyEntries);            var option = _AllOptions.FirstOrDefault(o => o.Short == parts.First().ToLower() || o.Long == parts.First().ToLower());            if (option == null)                throw new ArgumentException(string.Format(Unknown command line option {0}, arg));            if (option.RequiresParam && parts.Length == 1)                throw new ArgumentException(string.Format(Command line option {0} is missing required parameter, arg));            _AvailableOptions[option.Type] = option.RequiresParam ? parts.Last().Trim(_Quotes) : ;        }    }    public bool Has(Option option)    {        return _AvailableOptions.ContainsKey(option);    }    public string ValueOf(Option option)    {        string value;        if (!_AvailableOptions.TryGetValue(option, out value))            throw new ArgumentException(string.Format(Option {0} not present, option));        return value;    }}When you want to add a new option you have to do 2 things:Add a new entry to the enumAdd a new entry to the _AllOptions dictionaryFrom a DI perspective anything which needs access to the options just depends on ICommandLineOptions. If you wanted to get real fancy you could make CommandLineOption public, add an interface ICommandLineOption for it and make CommandLineOptions take a dependency on a collection of ICommandLineOptions. Then you could register all the options you want to support in the container rather than hard coding them in CommandLineOptions.