c#利用反射实现对类中的常量进行取值和对应常量的注释

简介: 原文:c#利用反射实现对类中的常量进行取值和对应常量的注释 因为业务需要,项目中有大量的错误码,还是通过分部类编写,报错之后返回一个错误码,无处可以方便的查询, 后来发现代码中每个错误码都有定义,而且都还有注释,因此考虑通过反射实现读取然后格式化形成错误码文档方便参阅。

原文:c#利用反射实现对类中的常量进行取值和对应常量的注释

因为业务需要,项目中有大量的错误码,还是通过分部类编写,报错之后返回一个错误码,无处可以方便的查询,
后来发现代码中每个错误码都有定义,而且都还有注释,因此考虑通过反射实现读取然后格式化形成错误码文档方便参阅。

读取注释

首先先读取注释,注释只要是标准的///生成的就能读取,因为每个项目可以生成一个对应的xml注释文档,这个功能默认
未开启,需要在要读取的类所在项目的名称上右键然后选择属性-生成-xml文档文件勾选上了并引入此项目,重新生成当前项目。
这样在输出目录中就存在了xml文档了。

实现代码

首先代码不多,其次,代码中有详细注释,就不多说了,代码如下

这是需要解析的类示例:

/// <summary>
/// 状态码类,存储状态码常量,这里的常量是http的状态码,用于反射解析
/// </summary>
public class StatusCode
{
    /// <summary>
    /// 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。
    /// </summary>
    public const int Continue = 100;
    /// <summary>
    /// 代表处理将被继续执行。
    /// </summary>
    public const int Processing = 102;
    /// <summary>
    /// 请求已成功,请求所希望的响应头或数据体将随此响应返回。
    /// </summary>
    public const int Ok = 200;
    /// <summary>
    /// 请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,且其URI已经随Location头信息返回。
    /// </summary>
    public const int Created = 201;
    /// <summary>
    /// 服务器已接受请求,但尚未处理。
    /// </summary>
    public const int Accepted = 202;
}

这是存储用的模型

/// 用于存储解析好的属性
/// </summary>
public class StatusCodeModel
{
    /// <summary>
    /// 状态码的全名
    /// </summary>
    public string FullName { get; set; }
    /// <summary>
    /// 状态码的名称
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 状态码的值
    /// </summary>
    public int Value { get; set; }
    /// <summary>
    /// 状态码的注释
    /// </summary>
    public string Note { get; set; }
}

核心实现:

public void Test()
{
    //读取注释

    //是从xx.xml文件里面读取,所以要确保要读取的类所在项目的属性-生成-xml文档文件勾选上了并引入此项目,重新生成当前项目
    string filePath = Environment.CurrentDirectory + @"\IceDog.DNL.CSharp.Grammar.xml";
    XmlDocument xml = new XmlDocument();
    //加载xml文件
    xml.Load(filePath);
    //用于存储解析出来的xml注释
    var dictNote = new Dictionary<string, string>();
    //可以查看xml文档格式然后可以通过如下XPath表达式获取相关节点内容
    var memebers = xml.SelectNodes("/doc/members/member");
    foreach (object m in memebers)
    {
        //判断是否转换类型成功
        if (m is XmlNode node)
        {
            //获取member节点的属性-名称
            XmlAttribute propName = node.Attributes["name"];
            string propNameValue = propName.Value;
            //里面还有一层summary节点,因为我们解析的是常量节点,
            //不会包含其他节点,所以不用进一步读取子节点
            var value = node.InnerText.Trim();
            //用于匹配的key
            var matchKey = "F:IceDog.DNL.CSharp.Grammar.Reflection.Constant.StatusCode.";
            //通过name值进行解析,目前发现的前缀有 F:field,M:method,T;type,P:property
            if (propNameValue.IndexOf(matchKey, StringComparison.Ordinal) > -1)
            {
                //去掉前缀和冒号,然后赋值
                dictNote[propNameValue.Substring(2)] =value;
            }
        }
    }
    //解析常量对象

    //存储解析的内容
    var codeList = new List<StatusCodeModel>();
    var constants = new ArrayList();
    Type type = typeof(StatusCode);
    //从规定的约束内搜索字段
    //约束有是静态成员,是公共成员,和返回父级的公共静态成员,
    FieldInfo[] infoList = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);

    foreach (FieldInfo info in infoList)
    {
        //按照要解析的字段的特性来判断,
        //常量是字面量,不可以在构造函数中初始化
        if (info.IsLiteral && !info.IsInitOnly)
        {
            constants.Add(info);
        }
    }
    //常量信息列表
    var constantInfoList = (FieldInfo[])constants.ToArray(typeof(FieldInfo));
    foreach (FieldInfo info in constantInfoList)
    {
        var scm = new StatusCodeModel
        {
            Value = (int)info.GetRawConstantValue(),
            Name = info.Name,
            FullName = info.DeclaringType.FullName + "." + info.Name
        };
        scm.Note = dictNote[scm.FullName];
        codeList.Add(scm);
    }
    //通过值进行升序排序
    codeList.Sort((m1, m2) => m1.Value - m2.Value);

    //接下来就可以进行自己需要的操作了,
    //这里是json序列化
    var str = JsonConvert.SerializeObject(codeList);
    Console.WriteLine(str);
}

输出结果如下

[{"FullName":"IceDog.DNL.CSharp.Grammar.Reflection.Constant.StatusCode.Continue","Name":"Continue","Value":100,"Note":"客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。"},{"FullName":"IceDog.DNL.CSharp.Grammar.Reflection.Constant.StatusCode.Processing","Name":"Processing","Value":102,"Note":"代表处理将被继续执行。"},{"FullName":"IceDog.DNL.CSharp.Grammar.Reflection.Constant.StatusCode.Ok","Name":"Ok","Value":200,"Note":"请求已成功,请求所希望的响应头或数据体将随此响应返回。"},{"FullName":"IceDog.DNL.CSharp.Grammar.Reflection.Constant.StatusCode.Created","Name":"Created","Value":201,"Note":"请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,且其URI已经随Location头信息返回。"},{"FullName":"IceDog.DNL.CSharp.Grammar.Reflection.Constant.StatusCode.Accepted","Name":"Accepted","Value":202,"Note":"服务器已接受请求,但尚未处理。"}]

参考文档

目录
相关文章
|
开发框架 .NET C#
C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
【10月更文挑战第9天】在 C#/.NET Core 中,有多种方法可以删除字符串的最后一个字符,包括使用 `Substring` 方法、`Remove` 方法、`ToCharArray` 与 `Array.Copy`、`StringBuilder`、正则表达式、循环遍历字符数组以及使用 LINQ 的 `SkipLast` 方法。
455 8
|
存储 C# 索引
C# 一分钟浅谈:数组与集合类的基本操作
【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
295 6
|
程序员 C# 数据库
C# 比较对象新思路,利用反射技术打造更灵活的比较工具
中途接手的项目,碰到需要在更新对象信息时比较并记录差异的需求,最变态的还有附加要求,怎么办?有没有既能满足需求又能对项目影响最小的方法呢?分享这个我封装的方法,一个利用反射技术打造的更灵活的比较工具
235 5
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
316 3
C#中的类和继承
C#中的类和继承
123 6
|
索引 C语言 存储
【C语言】B树和B+树的解析应用
【C语言】B树和B+树的解析应用
217 0
【C语言】B树和B+树的解析应用
|
Java 程序员 C#
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
169 0
|
开发框架 .NET 编译器
程序与技术分享:C#基础知识梳理系列三:C#类成员:常量、字段、属性
程序与技术分享:C#基础知识梳理系列三:C#类成员:常量、字段、属性
209 2
|
存储 C# 开发者
C# 编程基础:注释、变量、常量、数据类型和自定义类型
C# 编程基础:注释、变量、常量、数据类型和自定义类型
198 1
|
开发框架 .NET 编译器
C# 中的记录(record)类型和类(class)类型对比总结
C# 中的记录(record)类型和类(class)类型对比总结
501 0