/// <summary> /// Creates a dictionary that is populated with key/value pairs from a command line /// that supports syntax where options are provided in the form "/key=value". /// This method supports the ability to specify delimiter characters for options in /// the command line. /// </summary> /// <param name="arguments">Key/value pairs.</param> /// <param name="keyCharacter">A character that precedes a key.</param> /// <param name="valueCharacter">A character that separates a key from a value.</param> /// <returns></returns> public static CommandLineDictionary FromArguments(IEnumerable <string> arguments, char keyCharacter, char valueCharacter) { CommandLineDictionary cld = new CommandLineDictionary(); cld.KeyCharacter = keyCharacter; cld.ValueCharacter = valueCharacter; foreach (string argument in arguments) { cld.AddArgument(argument); } return(cld); }
/// <summary> /// Sets properties on an object from a series of key/value string /// arguments that are in the form "/PropertyName=Value", where the /// value is converted from a string into the property type. /// </summary> /// <param name="valueToPopulate">The object to set properties on.</param> /// <param name="args">The key/value arguments describing the property names and values to set.</param> /// <returns> /// Indicates whether the properties were successfully set. Reasons for failure reasons include /// a property name that does not exist or a value that cannot be converted from a string. /// </returns> /// <exception cref="System.ArgumentException">Thrown when one of the key/value strings cannot be parsed into a property.</exception> public static void ParseArguments(this object valueToPopulate, IEnumerable <string> args) { CommandLineDictionary commandLineDictionary = CommandLineDictionary.FromArguments(args); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(valueToPopulate); // Ensure required properties are specified. foreach (PropertyDescriptor property in properties) { // See whether any of the attributes on the property is a RequiredAttribute. if (property.Attributes.Cast <Attribute>().Any(attribute => attribute is RequiredAttribute)) { // If so, and the command line dictionary doesn't contain a key matching // the property's name, it means that a required property isn't specified. if (!commandLineDictionary.ContainsKey(property.Name)) { throw new ArgumentException("A value for the " + property.Name + " property is required."); } } } foreach (KeyValuePair <string, string> keyValuePair in commandLineDictionary) { // Find a property whose name matches the kvp's key, ignoring case. // We can't just use the indexer because that is case-sensitive. PropertyDescriptor property = MatchProperty(keyValuePair.Key, properties, valueToPopulate.GetType()); // If the value is null/empty and the property is a bool, we // treat it as a flag, which means its presence means true. if (string.IsNullOrEmpty(keyValuePair.Value) && (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?))) { property.SetValue(valueToPopulate, true); continue; } object valueToSet; // We support a limited set of collection types. Setting a List<T> // is one of the most flexible types as it supports three different // interfaces, but the catch is that we don't support the concrete // Collection<T> type. We can expand it to support Collection<T> // in the future, but the code will get a bit uglier. switch (property.PropertyType.Name) { case "IEnumerable`1": case "ICollection`1": case "IList`1": case "List`1": MethodInfo methodInfo = typeof(CommandLineParser).GetMethod("FromCommaSeparatedList", BindingFlags.Static | BindingFlags.NonPublic); Type[] genericArguments = property.PropertyType.GetGenericArguments(); MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments); valueToSet = genericMethodInfo.Invoke(null, new object[] { keyValuePair.Value }); break; default: TypeConverter typeConverter = TypeDescriptor.GetConverter(property.PropertyType); if (typeConverter == null || !typeConverter.CanConvertFrom(typeof(string))) { throw new ArgumentException("Unable to convert from a string to a property of type " + property.PropertyType + "."); } valueToSet = typeConverter.ConvertFrom(keyValuePair.Value); break; } property.SetValue(valueToPopulate, valueToSet); } }