csharp 基于简单反射的Cli Cmdline解析器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csharp 基于简单反射的Cli Cmdline解析器相关的知识,希望对你有一定的参考价值。
namespace DEMon
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public sealed class CmdHelpAttribute : Attribute
{
public CmdHelpAttribute(string help) : base()
{
CmdHelp = help;
}
public string CmdHelp { get; set; }
}
public class CommandLineParserResult<T>
where T : new()
{
public T Switches = default(T);
public int ArgCount = 0;
public List<string> ErrorMessages = new List<string>();
public static bool IsSwitch(string arg)
{
if (arg.Length < 1)
{
return false;
}
char c = arg[0];
return c == '-' || c == '/';
}
public void ShowErrors()
{
foreach (var errorMessage in ErrorMessages)
{
Log.WriteLine("ERROR: " + errorMessage);
}
}
public static void ShowGeneralHelp()
{
// General sections are defined as internal or private properties with CmdHelp attribute
// the name of the attribute defines the "section" name and the attribute content the actual help
// These are always shown first
var switchesHelp = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => Attribute.IsDefined(p, typeof(CmdHelpAttribute)))
.OrderBy(p => p.Name)
.Select(p => new { Prop = p, Help = (CmdHelpAttribute)p.GetCustomAttribute(typeof(CmdHelpAttribute)) })
.ToList();
switchesHelp.ForEach(gh =>
{
Log.WriteLine(gh.Prop.Name);
Log.WriteLine(gh.Help.CmdHelp);
});
}
public static void ShowHelpForSwitches(bool showJustOptionsNames)
{
// Normal "switches" (commands/options) are public properties.
// These are shown AFTER general help sections:
var switchesHelp = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p => p.Name)
.ToList();
Log.WriteLine("Switches:");
switchesHelp.ForEach(gh =>
{
var switchText = $"/{gh.Name}";
switch (Type.GetTypeCode(gh.PropertyType))
{
case TypeCode.String:
case TypeCode.Object:
switchText += " <string value>";
break;
case TypeCode.Int64:
switchText += " <long value>";
break;
case TypeCode.Int32:
switchText += " <int value>";
break;
case TypeCode.Boolean:
// nothing
break;
default:
break;
}
if (showJustOptionsNames)
{
Log.WriteLine(switchText);
}
else if (Attribute.IsDefined(gh, typeof(CmdHelpAttribute)))
{
Log.WriteLine("");
Log.WriteLine(switchText);
var cmdHelp = (CmdHelpAttribute)Attribute.GetCustomAttribute(gh, typeof(CmdHelpAttribute));
Log.WriteLine(cmdHelp.CmdHelp);
}
});
if (!showJustOptionsNames)
{
Log.WriteLine("\r\n/list - dumps a succinct list of ALL switches (including undocumented)");
}
}
public static void ShowHelp()
{
ShowGeneralHelp();
ShowHelpForSwitches(showJustOptionsNames: false);
}
public static CommandLineParserResult<T> Parse(string[] args)
{
var result = new CommandLineParserResult<T>();
var switches = new T();
var allProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(k => k.Name, v => v, StringComparer.OrdinalIgnoreCase);
try
{
for (int i = 0; i < args.Length; i++)
{
string arg = args[i];
if (IsSwitch(arg))
{
PropertyInfo property = null;
string name = arg.Substring(1);
if (name == "?" || name.Equals("help", StringComparison.OrdinalIgnoreCase))
{
// show help (General help sections and then switches with CmdHelp)
ShowHelp();
return result;
}
if (name.Equals("list", StringComparison.OrdinalIgnoreCase))
{
// show bare list of options (with or without CmdHelp)
ShowHelpForSwitches(showJustOptionsNames: true);
return result;
}
// Has to be a public property
if (!allProperties.TryGetValue(name, out property))
{
result.ErrorMessages.Add($"Unknown switch '{arg}'. Use /? for help or /list for a full list of all options (including undocumented)");
return result;
}
string value;
switch (Type.GetTypeCode(property.PropertyType))
{
case TypeCode.String:
case TypeCode.Object:
i++;
if (i >= args.Length)
{
result.ErrorMessages.Add($"Value for switch '{arg}' was not provided and is required");
return result;
}
value = args[i];
if (property.PropertyType == typeof(string))
{
property.SetValue(switches, value, null);
}
result.ArgCount++;
break;
case TypeCode.Boolean:
property.SetValue(switches, true, null);
result.ArgCount++;
break;
case TypeCode.Int32:
i++;
int intValue;
if (i >= args.Length)
{
result.ErrorMessages.Add($"Integer value for switch '{arg}' was not provided and is required");
return result;
}
if (!int.TryParse(args[i], out intValue))
{
result.ErrorMessages.Add($"The provided value '{args[i]}' is not a valid integer for switch '{property.Name}'.");
return result;
}
property.SetValue(switches, intValue, null);
result.ArgCount++;
break;
case TypeCode.Int64:
i++;
long longValue;
if (i >= args.Length)
{
result.ErrorMessages.Add($"Long value for switch '{arg}' was not provided and is required");
return result;
}
if (!long.TryParse(args[i], out longValue))
{
result.ErrorMessages.Add($"The provided value '{args[i]}' is not a valid long for switch '{property.Name}'.");
return result;
}
property.SetValue(switches, longValue, null);
result.ArgCount++;
break;
default:
result.ErrorMessages.Add($"Switch parsing for value type '{property.PropertyType}' not implemented.");
break;
}
}
else
{
result.ErrorMessages.Add($"Unknown switch '{arg}'. Use /? for help or /list for a full list of all options (including undocumented)");
return result;
}
}
if (result.ArgCount == 0)
{
ShowHelp();
return result;
}
// Assign the valid switches we just got to Switches
result.Switches = switches;
return result;
}
finally
{
// We can get here if there's an early termination of the
if (result.ErrorMessages.Count > 0)
{
result.ShowErrors();
}
}
}
}
}
以上是关于csharp 基于简单反射的Cli Cmdline解析器的主要内容,如果未能解决你的问题,请参考以下文章