• C#基础--Attribute(标签) 和 reflect(反射) 应用


    1.Attribute的定义与作用:

             公共语言运行时允许你添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为.比如在WCF中常用的序列化。比如添加的[DataMenber]就是一种标签。以及xml序列化也是一样的。

       2.Reflect的定义与作用

             反射可以用于观察并修改程序在运行时的执行。一个reflection-oriented程序组件可以监测一个范围内的代码执行情况,可以根据期望的目标与此相关的范围修改本身。这通常是通过在运行时动态分配程序代码实现。在面向对象的编程语言如Java中,反射允许在编译期间不知道接口的名称,字段、方法的情况下在运行时检查类、接口、字段和方法。它还允许的实例化新对象和调用的方法。(摘抄至维基百科). 其实可以简单理解为,通过反射可以很轻易的获取到一个类中的所有方法属性字段状态。

         3.为了更方便的认识反射以及Attribute的应用,下面有个例子.

            业务背景如下: 有个Person对象,目前只有Name和Num属性,然后要用方法校验Person对象的合法性。

            判断规则:  1.Name必填,若Name为空,则提示"Name不能为空";

                           2.Num可以为空,但是若有数据的话必须由数字组成,若为非数字字符串则弹出"格式不正确".

              3.Address可以为空,若有数据的话长度不能超过12,若超过12则提示"Address 长度不能超过12"

           需要考虑: 对于Person类,以后熟悉的判断规则可能会变化,也可能增加属性,减少属性。

         4.解决方案一:

          对于常规解决方案,可能大家会想到。目前只有Name,Address,Num三个属性,每次我就写三个代码段分别对Name,Address,Num验证。如果增加属性, 比如增加了公共属性EmailAddress,那么就必须添加验证字符串是否是邮箱的代码,若删除了某个属性,就吧判断该属性是否合格的代码删除掉不进行判断。这个也是我最开始想到的方法。不过这样不好的地方是可维护性不好。改动太大了。而且会导致重复代码。不如如果需求改成Address不能为空了。那么在判断address是否合法的时候还要去添加判断不能为空的情况。

        5.解决方案二:

          为了解决方案一的问题,其实我们可以通过Attribute和反射来实现。思路如下:

         5.1 首先我们创建一个PersonCheckAttribute对象,他继承与System.Attribute. 

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ReflectTest.Model
    {
        public class PersonCheckAttribute:Attribute
        {
    
            // 三个bool变量用于确定是有要验证这些信息
            private bool checkEmpty = false; //是否为空
            private bool checkMaxLength = false; //最大长度
            private bool checkRegex = false; //用正则表达式验证参数(是邮箱,是否刷数字)
    
            private int maxLength = 0;
            private string regexStr = string.Empty;
    
    
            public bool CheckEmpty
            {
                get { return this.checkEmpty; }
                set { this.checkEmpty = value; }
            }
    
            public bool CheckMaxLength
            {
                get { return this.checkMaxLength; }
                set { this.checkMaxLength = value; }
            }
    
            public bool CheckRegex
            {
                get { return this.checkRegex; }
                set { this.checkRegex = value; }
            }
    
            public int MaxLength
            {
                get { return this.maxLength; }
                set { this.maxLength = value; }
            }
    
            public string RegexStr
            {
                get { return this.regexStr; }
                set { this.regexStr = value; }
            }
        }
    }
    复制代码

         5.2 然后我们来添加Person类。注意里面的标签。

        Tips:我在里面打了标签,对于5.1的PersonCheckAttribute类中bool变量用来确定是否要进行验证,公共属性用来存取判断参数。
      

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ReflectTest.Model
    {
        public class Person
        {
            [PersonCheck(CheckEmpty=true)]
            public string Name { get; set; }
    
            [PersonCheck(CheckRegex = true, RegexStr = "^[0-9]*[1-9][0-9]*$")]
            public string Num { get; set; }
    
            [PersonCheck(CheckMaxLength = true, MaxLength = 12)]
            public string Address { get; set; }
        }
    }
    复制代码

       5.3 属性已经搞定,下面来看看,如何通过反射来获取类的公共属性,以及每个属性的Attribute。
       

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using ReflectTest.Model;
    using System.Reflection;
    using System.Text.RegularExpressions;
    
    namespace ReflectTest.Business
    {
        public class PersonCheck
        {
            // 获取所有的错误提示
            public static string GetErrorMessage(Person person)
            {
                PropertyInfo[] propertyInfos = person.GetType().GetProperties(); // 获取一个类的所有属性
                string errorMsg = string.Empty;
    
                //对所有公用属性(Name,Num,Address)遍历
                foreach (PropertyInfo info in propertyInfos)
                {
                    errorMsg += GetSignalPropertity(info, person);
                }
                return errorMsg;
            }
    
            // 获取单个公共属性的Attribute.也就是model类里面的[PersonCheck]信息
            private static string GetSignalPropertity(PropertyInfo propertyInfo,Person person)
            {
                // 因为对于此示例,每个Properties(属性)只有一个Attribute(标签),所以用了first()来获取,
                // 不过有一点,就是必须在属性里面添加[PersonCheck]标签,但是可以不设置表情里面的字段.因为没有的.GetCustomAttributes()返回为null.指向first会报错.
                // PersonCheckAttribute attribute = propertyInfo.GetCustomAttributes().First() as PersonCheckAttribute; 
    
                string errorMsg = string.Empty;
    
                //以下的if语句是判断标签里面的设置,设置了什么就执行什么数据校验
                PersonCheckAttribute attribute = propertyInfo.GetCustomAttribute(typeof(PersonCheckAttribute)) as PersonCheckAttribute;
                if (attribute != null)
                {
                    if (attribute.CheckEmpty)
                    {
                        string obj = propertyInfo.GetValue(person) as string;
                        if (string.IsNullOrEmpty(obj))
                        {
                            errorMsg += Environment.NewLine + string.Format("{0} 不能为空", propertyInfo.Name);
                        }
                    }
    
                    if (attribute.CheckMaxLength)
                    {
                        string obj = propertyInfo.GetValue(person) as string;
                        if (obj != null && obj.Length > attribute.MaxLength)
                        {
                            errorMsg += Environment.NewLine + string.Format("{0} 不能超过最大程度{1}", propertyInfo.Name, attribute.MaxLength);
                        }
                    }
    
                    // 对于判断数字邮箱都可以通过正则表达式
                    if (attribute.CheckRegex)
                    {
                        string obj = propertyInfo.GetValue(person) as string;
                        Regex regex = new Regex(attribute.RegexStr);
                        if (obj != null && !regex.IsMatch(obj))
                        {
                            errorMsg += Environment.NewLine + string.Format("{0} 格式不对", propertyInfo.Name);
                        }
                    }
                }
                return errorMsg;
            }
        }
    }
    复制代码

      5.4 下面来看运行结果。

         5.4.1 若Person p = new Person(){Num=“abc”};调用GetErrorMessage(Person person)方法后运行结果如下。Person不能为空.
         

        5.4.2 若Person p = new Person(){Num=“123”,Address=“chengdu,Tianfu SoftwarePark”};调用GetErrorMessage(Person person)方法后运行结果如下。Person不能为空.

      

      6.分析反射的好处:

         如果需求修改,Address不能为空了,我只需要在Address的标签里面设置CheckEmpty=true,而不需要重新修改逻辑代码。同理,如果要给Person类添加属性EmailAddress,那么只需要在Person类里面添加属性EmailAddress设置标签为【PersonCheck(CheckRegexStr = true,regexStr="*")】,*未判断是否是合法有效的正则表达式即可。

      7.总结:

           其实小弟也是刚接触反射。有很多东西也是一知半解。目前对反射的认识也就这么多,有其他大牛应用更多这方面的,如果有什么好的想法和资料也请大家分享一下,相互学习,相互提升。代码链接:https://github.com/FourLeafClover/AttributeAndReflect

  • 相关阅读:
    with admin option 和 with grant option 的区别 (转)
    压缩数据块
    建表时pctfree和pctused参数作用
    Oracle哪些错误会写进alert日志
    Oracle 差异、增量、零级备份
    模拟原生的promise
    react-loadable 实现组件按需加载
    react 中配置 http-proxy-middleware
    craco 配置 less.module
    ESLint: 'React' was used before it was defined.(no-use-before-define)
  • 原文地址:https://www.cnblogs.com/sjqq/p/6930743.html
Copyright © 2020-2023  润新知