@@ -121,6 +121,52 @@ public ArgumentAttribute(char shortName, string longName, string helpText = null
121121 public char ShortName { get ; set ; }
122122 }
123123
124+ /// <summary>
125+ /// Encapsulates argument names and help text.
126+ /// </summary>
127+ public class ArgumentInfo
128+ {
129+ /// <summary>
130+ /// Initializes a new instance of the <see cref="ArgumentInfo"/> class.
131+ /// </summary>
132+ /// <param name="shortName">The short name of the argument.</param>
133+ /// <param name="longName">The long name of the argument.</param>
134+ /// <param name="helpText">The help text for the argument.</param>
135+ /// <param name="property">The property with which the argument is associated.</param>
136+ public ArgumentInfo ( char shortName , string longName , string helpText , PropertyInfo property )
137+ {
138+ ShortName = shortName ;
139+ LongName = longName ;
140+ HelpText = helpText ;
141+ Property = property ;
142+ }
143+
144+ /// <summary>
145+ /// Gets the help text for the argument.
146+ /// </summary>
147+ public string HelpText { get ; }
148+
149+ /// <summary>
150+ /// Gets a value indicating whether the argument backing Type is a collection.
151+ /// </summary>
152+ public bool IsCollection => Property . PropertyType . IsArray || ( Property . PropertyType . IsGenericType && Property . PropertyType . GetGenericTypeDefinition ( ) == typeof ( List < > ) ) ;
153+
154+ /// <summary>
155+ /// Gets the long name of the argument.
156+ /// </summary>
157+ public string LongName { get ; }
158+
159+ /// <summary>
160+ /// Gets the property with which the argument is associated.
161+ /// </summary>
162+ public PropertyInfo Property { get ; }
163+
164+ /// <summary>
165+ /// Gets the short name of the argument.
166+ /// </summary>
167+ public char ShortName { get ; }
168+ }
169+
124170 /// <summary>
125171 /// Provides static methods used to retrieve the command line arguments and operands with which the application was
126172 /// started, as well as a Type to contain them.
@@ -162,21 +208,25 @@ private Arguments(string commandLineString, List<KeyValuePair<string, string>> a
162208 }
163209
164210 /// <summary>
165- /// Gets the target Type, if applicable.
211+ /// Gets a dictionary containing the arguments and values specified in the command line arguments with which the
212+ /// application was started.
166213 /// </summary>
167- public Type TargetType { get ; }
214+ /// <remarks>
215+ /// This dictionary contains argument key/value pairs compiled from the <see cref="ArgumentList"/> and checked against
216+ /// the <see cref="TargetType"/> to combine duplicated pairs into lists where the backing property is a collection, and
217+ /// to overwrite where the backing property is not a collection.
218+ /// </remarks>
219+ public Dictionary < string , object > ArgumentDictionary { get ; }
168220
169221 /// <summary>
170222 /// Gets the list of arguments specified in the command line arguments with which the application was started.
171223 /// </summary>
224+ /// <remarks>
225+ /// This list contains each argument key/value pair as supplied in the original string, preserving the original order
226+ /// and any duplicated pairs.
227+ /// </remarks>
172228 public List < KeyValuePair < string , string > > ArgumentList { get ; }
173229
174- /// <summary>
175- /// Gets a dictionary containing the arguments and values specified in the command line arguments with which the
176- /// application was started.
177- /// </summary>
178- public Dictionary < string , object > ArgumentDictionary { get ; }
179-
180230 /// <summary>
181231 /// Gets the command line string from which the arguments were parsed.
182232 /// </summary>
@@ -187,6 +237,11 @@ private Arguments(string commandLineString, List<KeyValuePair<string, string>> a
187237 /// </summary>
188238 public List < string > OperandList { get ; private set ; }
189239
240+ /// <summary>
241+ /// Gets the target Type, if applicable.
242+ /// </summary>
243+ public Type TargetType { get ; }
244+
190245 /// <summary>
191246 /// Gets the argument value corresponding to the specified <paramref name="index"/>.
192247 /// </summary>
@@ -201,7 +256,8 @@ public object this[int index]
201256 }
202257
203258 /// <summary>
204- /// Gets the argument value corresponding to the specified <paramref name="key"/> from the <see cref="ArgumentDictionary"/> property.
259+ /// Gets the argument value corresponding to the specified <paramref name="key"/> from the
260+ /// <see cref="ArgumentDictionary"/> property.
205261 /// </summary>
206262 /// <param name="key">The key for which the value is to be retrieved.</param>
207263 /// <returns>The argument value corresponding to the specified key.</returns>
@@ -213,43 +269,6 @@ public object this[string key]
213269 }
214270 }
215271
216- private static Dictionary < string , object > GetArgumentDictionary ( List < KeyValuePair < string , string > > argumentList , Type targetType = null )
217- {
218- var dict = new ConcurrentDictionary < string , object > ( ) ;
219- var argumentInfo = targetType == null ? new List < ArgumentInfo > ( ) : GetArgumentInfo ( targetType ) ;
220-
221- foreach ( var arg in argumentList )
222- {
223- var info = argumentInfo . Where ( i => i . ShortName . ToString ( ) == arg . Key || i . LongName == arg . Key ) . SingleOrDefault ( ) ;
224-
225- if ( info != default ( ArgumentInfo ) )
226- {
227- bool added = false ;
228-
229- foreach ( var k in new [ ] { info . ShortName . ToString ( ) , info . LongName } )
230- {
231- if ( dict . ContainsKey ( k ) )
232- {
233- dict . AddOrUpdate ( k , arg . Value , ( key , existingValue ) => info . IsCollection ? ( ( List < object > ) existingValue ) . Concat ( new [ ] { arg . Value } ) . ToList ( ) : ( object ) arg . Value ) ;
234- added = true ;
235- break ;
236- }
237- }
238-
239- if ( ! added )
240- {
241- dict . TryAdd ( arg . Key , info . IsCollection ? new List < object > ( new [ ] { arg . Value } ) : ( object ) arg . Value ) ;
242- }
243- }
244- else
245- {
246- dict . AddOrUpdate ( arg . Key , arg . Value , ( key , existingValue ) => arg . Value ) ;
247- }
248- }
249-
250- return dict . ToDictionary ( a => a . Key , a => a . Value ) ;
251- }
252-
253272 /// <summary>
254273 /// Retrieves a collection of <see cref="ArgumentInfo"/> gathered from properties in the target <paramref name="type"/>
255274 /// marked with the <see cref="ArgumentAttribute"/><see cref="Attribute"/> along with the short and long names and help text.
@@ -268,13 +287,11 @@ private static Dictionary<string, object> GetArgumentDictionary(List<KeyValuePai
268287
269288 if ( attribute != default ( CustomAttributeData ) )
270289 {
271- retVal . Add ( new ArgumentInfo ( )
272- {
273- ShortName = ( char ) attribute . ConstructorArguments [ 0 ] . Value ,
274- LongName = ( string ) attribute . ConstructorArguments [ 1 ] . Value ,
275- HelpText = ( string ) attribute . ConstructorArguments [ 2 ] . Value ,
276- Type = property . PropertyType ,
277- } ) ;
290+ retVal . Add ( new ArgumentInfo (
291+ shortName : ( char ) attribute . ConstructorArguments [ 0 ] . Value ,
292+ longName : ( string ) attribute . ConstructorArguments [ 1 ] . Value ,
293+ helpText : ( string ) attribute . ConstructorArguments [ 2 ] . Value ,
294+ property : property ) ) ;
278295 }
279296 }
280297
@@ -413,16 +430,7 @@ public static void Populate(Type type, Arguments arguments, bool clearExistingVa
413430 }
414431 else if ( propertyType . IsArray || ( propertyType . IsGenericType && propertyType . GetGenericTypeDefinition ( ) == typeof ( List < > ) ) )
415432 {
416- // if the property is an array or list, convert the value to an array or list of the matching type. start
417- // by converting atomic values to a list containing a single value, just to simplify processing.
418- if ( valueIsList )
419- {
420- convertedValue = value ;
421- }
422- else
423- {
424- convertedValue = new List < object > ( new object [ ] { value } ) ;
425- }
433+ convertedValue = value ;
426434
427435 // next, create a list with the same type as the target property
428436 Type valueType ;
@@ -526,6 +534,43 @@ private static void ClearProperties(Dictionary<string, PropertyInfo> properties)
526534 }
527535 }
528536
537+ private static Dictionary < string , object > GetArgumentDictionary ( List < KeyValuePair < string , string > > argumentList , Type targetType = null )
538+ {
539+ var dict = new ConcurrentDictionary < string , object > ( ) ;
540+ var argumentInfo = targetType == null ? new List < ArgumentInfo > ( ) : GetArgumentInfo ( targetType ) ;
541+
542+ foreach ( var arg in argumentList )
543+ {
544+ var info = argumentInfo . Where ( i => i . ShortName . ToString ( ) == arg . Key || i . LongName == arg . Key ) . SingleOrDefault ( ) ;
545+
546+ if ( info != default ( ArgumentInfo ) )
547+ {
548+ bool added = false ;
549+
550+ foreach ( var k in new [ ] { info . ShortName . ToString ( ) , info . LongName } )
551+ {
552+ if ( dict . ContainsKey ( k ) )
553+ {
554+ dict . AddOrUpdate ( k , arg . Value , ( key , existingValue ) => info . IsCollection ? ( ( List < object > ) existingValue ) . Concat ( new [ ] { arg . Value } ) . ToList ( ) : ( object ) arg . Value ) ;
555+ added = true ;
556+ break ;
557+ }
558+ }
559+
560+ if ( ! added )
561+ {
562+ dict . TryAdd ( arg . Key , info . IsCollection ? new List < object > ( new [ ] { arg . Value } ) : ( object ) arg . Value ) ;
563+ }
564+ }
565+ else
566+ {
567+ dict . AddOrUpdate ( arg . Key , arg . Value , ( key , existingValue ) => arg . Value ) ;
568+ }
569+ }
570+
571+ return dict . ToDictionary ( a => a . Key , a => a . Value ) ;
572+ }
573+
529574 private static List < KeyValuePair < string , string > > GetArgumentList ( string commandLineString )
530575 {
531576 var argumentList = new List < KeyValuePair < string , string > > ( ) ;
@@ -640,37 +685,6 @@ private static PropertyInfo GetOperandsProperty(Type type)
640685 }
641686 }
642687
643- /// <summary>
644- /// Encapsulates argument names and help text.
645- /// </summary>
646- public class ArgumentInfo
647- {
648- /// <summary>
649- /// Gets or sets the help text for the argument.
650- /// </summary>
651- public string HelpText { get ; set ; }
652-
653- /// <summary>
654- /// Gets or sets the long name of the argument.
655- /// </summary>
656- public string LongName { get ; set ; }
657-
658- /// <summary>
659- /// Gets or sets the short name of the argument.
660- /// </summary>
661- public char ShortName { get ; set ; }
662-
663- /// <summary>
664- /// Gets or sets the backing Type of the argument.
665- /// </summary>
666- public Type Type { get ; set ; }
667-
668- /// <summary>
669- /// Gets a value indicating whether the argument backing Type is a collection.
670- /// </summary>
671- public bool IsCollection => Type . IsArray || ( Type . IsGenericType && Type . GetGenericTypeDefinition ( ) == typeof ( List < > ) ) ;
672- }
673-
674688 /// <summary>
675689 /// Indicates that the property is to be used as the target for automatic population of command line operands when invoking
676690 /// the <see cref="Arguments.Populate(string, bool, string)"/> method.
@@ -679,4 +693,4 @@ public class ArgumentInfo
679693 public class OperandsAttribute : Attribute
680694 {
681695 }
682- }
696+ }
0 commit comments