# Parsing Command Line Arguments in C# The following is an automatic way of parsing command line arguments. It relies on reflection to automatically set properties based on a list of argument definitions, similarly how the Argu library does it (it was inspired by Argu). Maybe with all the issues I've had with Argu, this way might be preferable in F# as well? Using FParsec would be another option. See [[FParsec Examples]]. ```csharp using System; using System.Linq; using System.Collections.Generic; using System.Text; using Common; using System.Linq.Expressions; using System.Reflection; public static class Extensions { public static IEnumerable<IEnumerable<T>> ChunkBySize<T>(this IEnumerable<T> source, int chunkSize) { if (chunkSize <= 1) { throw new ArgumentException("Chunk must be positive."); } using (IEnumerator<T> e = source.GetEnumerator()) { while (e.MoveNext()) { T[] chunk = new T[chunkSize]; int i = 0; do { chunk[i] = e.Current; i = i + 1; } while (i < chunkSize && e.MoveNext()); yield return i == chunkSize ? chunk : chunk.Take(i); } } } } public class Arguments { private class ArgumentDefinition { public string Argument { get; set; } public bool Required { get; set; } public PropertyInfo PropertyInfo { get; private set; } public string DefaultValue { get; set; } public ArgumentDefinition SetPropertyInfoFromExpression(Expression<Func<Arguments, string>> propertyExpression) { PropertyInfo = PropertyMapperHelpers.GetPropertyInfoFromLambda(propertyExpression); return this; } public bool IsSet(Arguments a) => PropertyInfo.GetValue(a, null) != null; } public const string Help = @"Description of arguments"; private readonly static List<ArgumentDefinition> ArgumentDefinitions = new List<ArgumentDefinition> { new ArgumentDefinition { Argument = "--input", Required = true } .SetPropertyInfoFromExpression(a => a.InputDirectory), new ArgumentDefinition { Argument = "--template", Required = true } .SetPropertyInfoFromExpression(a => a.Template), new ArgumentDefinition { Argument = "--output", DefaultValue = "Output" } .SetPropertyInfoFromExpression(a => a.OutputDirectory), }; public static void Process( Action<Arguments> onSuccess, Action<IEnumerable<string>> onFailure, string[] rawArguments) { IEnumerable<IEnumerable<string>> pairedArgs = rawArguments.ChunkBySize(2); List<string> validationErrors = new List<string>(); Arguments arguments = new Arguments(); foreach (IEnumerable<string> argumentPair in pairedArgs) { ArgumentDefinition mapping = ArgumentDefinitions.FirstOrDefault(d => d.Argument == argumentPair.First()); if (mapping == null) { validationErrors.Add(quot;{argumentPair.First()} is not a supported argument."); } else { mapping.PropertyInfo.SetValue(arguments, argumentPair.ElementAtOrDefault(1) ?? "", null); } } IEnumerable<ArgumentDefinition> postProcessingDefinitions = ArgumentDefinitions .Where(d => d.DefaultValue != null && d.IsSet(arguments) == false); foreach (ArgumentDefinition d in postProcessingDefinitions) { d.PropertyInfo.SetValue(arguments, d.DefaultValue, null); } validationErrors.AddRange( ArgumentDefinitions .Where(d => d.Required && d.IsSet(arguments) == false) .Select(mapping => quot;{mapping.Argument} argument is missing.")); if (validationErrors.Any()) { onFailure(validationErrors); } else { onSuccess(arguments); } } public string InputDirectory { get; set; } public string Template { get; set; } public string OutputDirectory { get; set; } } ```