Tuesday, March 16, 2010

Using Reflection to Automatically Parse Command Line Arguments

It's been a while since I wrote a real command line application (long before C# got reflection) and haven't had to parse command line arguments in quite some time. Here is my new ParseArgs() function using reflection. The basic assumption is that arguments are being used to set static Properties of the entry point class.

public static void ParseArgs (string [] args)
{
    // Arguments are assumed to come in pairs, e.g.:
    //    program /arg1 arg_value1 /arg2 arg_value2
    Validate.True ("Number of arguments is even",
        0 == (args.Length & 1));

    MethodInfo method_info;
    string convert_method;
    object [] parameters = new object [1];
    Type [] string_type = { typeof (String) };

    // Get the type for where ever this is being called
    //   There must be a better way to do this
    Type this_type = new StackTrace ().GetFrame (0).GetMethod ().ReflectedType;

    try
    {
        // Loop through each argument pair (i += 2), assumed format is:
        //    program /arg1 arg_value1 /arg2 arg_value2
        // Other formats are not supported.  arg1, arg2, etc are assumed
        // to be static properties for this_type.
        for (int i = 0; i < args.Length; i += 2)
        {
            // Trim the leading slash on the property name
            Validate.True ("Arguments start with a slash(/)",
                args [i] [0].Equals ('/'));
            string prop_name = args [i].TrimStart ('/');

            // Get the property associated with this argument. We don't
            // have an instance, so only static properties are appropriate.
            PropertyInfo pi = this_type.GetProperty (prop_name, 
                BindingFlags.IgnoreCase | 
                BindingFlags.Static | 
                BindingFlags.SetProperty | 
                BindingFlags.Public);

            // If there is no match then just continue
            if (null == pi)
                continue;

            // Get the Setter mthod for this property
            method_info = pi.GetSetMethod ();

            // Conversion is based on the Convert class.

            // Here we try to assemble the name for the appropriate
            // method of Convert. Methods are of the form "To____(String)"
            // where "____" is the Type we are trying to convert the
            // string two.
            convert_method = "To" + pi.PropertyType.Name;

            // Get the Convert method
            MethodInfo m = typeof (Convert).GetMethod (
                convert_method, string_type);

            // We will pass the current arg_value to the Convert method 
            parameters [0] = args [i + 1];

            // Invoke the convert method.  The result will be passed to
            // the property's Setter method.
            parameters [0] = m.Invoke (typeof (Convert), parameters);

            // Invoke the setter method on this_type
            method_info.Invoke (this_type, parameters);
        }
    }
    catch (Exception ex)
    {
        new LoggedException (ex, "Failed to parse command line arguments").Log ();
    }
}

1 comment:

  1. Hi Jerry! I am wondering if you even still maintain or think about this site anymore... but thank you very much for this and your previous posts.

    Did you ever get around to finishing this framework? I am looking to do something very similar right now. I work in automation. Are you still performing automated testing as part of your duties?

    -Nelson (brownnrl _a_t_ -->) (gmail)

    ReplyDelete