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解析器的主要内容,如果未能解决你的问题,请参考以下文章

从另一个基于 C 的可执行文件获取 MAC os 中的进程 cmdline

csharp Type的反射GetField

csharp Url参数方法反射

csharp Url参数方法反射

csharp 基于马尔可夫链的简单文本生成器

csharp 基于马尔可夫链的简单文本生成器(二)