zoukankan      html  css  js  c++  java
  • C#高级编程之特性

    特性定义

    MSDN的描述:使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。 将特性与程序实体相关联后,可以在运行时使用反射这项技术查询特性。

    参考此处作者的解释

    https://www.cnblogs.com/chenxizhaolu/p/9497768.html

    1.特性就是为了支持对象添加一些自我描述的信息,不影响类封装的前提添加额外信息。如果你用这个信息,那特性就有用;如果你不需要这个信息,那么这个特性就没用。

    2.特性的基类:Attribute。例如:Obsolete特性,提出警告信息或错误信息,特性可以影响编译、影响运行。

    3.特性类通常用Attribute结尾,在使用的时候可以用全称,也可以去掉这个结尾,也可以加上小括号显示调用构造函数,如果不加小括号默认调用无参构造函数,也可以在括号内直接给属性或字段赋值。

    4.特性往往只能修饰一个对象一次,需要设置属性的属性的时候,需要给属性添加AttributeUsage属性,可以用来设置:是否允许多次修饰、修饰对象的类别(类or字段等)

    5.DLL文件=IL中间语言+metadata元数据,特性信息会被编译到元数据中。我们可以通过使用ILSpy工具看到具体信息。

    特性的使用

    使用场景1:

    这里以通过特性获取类或成员添加描述信息,然后在使用的时候拿到该信息为例:

     第一步:定义一个特性类    

        public class CustomAttribute : Attribute
        {
            public int YearInfo { get; set; }
            public CustomAttribute()
            {
                Console.WriteLine("无参数构造函数");
    
            }
            public CustomAttribute(int i)
            {
                YearInfo = i;
                Console.WriteLine("int 类型的构造函数");
            }
            public void Show(string typeinfo)
            {
                Console.WriteLine("********************");
                Console.WriteLine($"特性修饰的粒度{typeinfo}");
                Console.WriteLine($"所在年信息为:{YearInfo}");
                Console.WriteLine("通过反射调用特性中的方法");
            }
    
        }

    第二步:创建特性类实例,【里面包含着验证指定粒度的Model模型(类型、属性、方法)需要的数据,后面数据验证场景会讲到】
    此处只是Model的不同粒度上附加数据。这里分别将特性附加于类型、属性和方法上。

        public class Student
        {
            public string Id { get; set; }
            public string Name { get; set; }
            public void Answer(string message)
            {
                Console.WriteLine(message);
            }
        }
        [Custom(2018)]
        class StudentVIP:Student
        {
            [Custom(2019)]
            public string VIPGroup { get; set; }
            [Custom(2020)]
            public void HomeWork()
            {
                Console.WriteLine("Homework");
            }
            public long Salary { get; set; }
        }

    通过ILSpy反编译工具可以看到:标记了特性的元素,都会在元素内部生成一个.custom,但是C#不能在元素内部调用

    .class private auto ansi beforefieldinit LearningAttribute.StudentVIP
        extends LearningAttribute.Student
    {
        .custom instance void LearningAttribute.CustomAttribute::.ctor(int32) = (
            01 00 e2 07 00 00 00 00
        )
        // Fields
        .field private string '<VIPGroup>k__BackingField'
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = (
            01 00 00 00 00 00 00 00
        )
        .field private int64 '<Salary>k__BackingField'
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = (
            01 00 00 00 00 00 00 00
        )
    
        // Methods
        .method public hidebysig specialname 
            instance string get_VIPGroup () cil managed 
        {
            .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
                01 00 00 00
            )
            // Method begins at RVA 0x2441
            // Code size 7 (0x7)
            .maxstack 8
    
            IL_0000: ldarg.0
            IL_0001: ldfld string LearningAttribute.StudentVIP::'<VIPGroup>k__BackingField'
            IL_0006: ret
        } // end of method StudentVIP::get_VIPGroup
    
        .method public hidebysig specialname 
            instance void set_VIPGroup (
                string 'value'
            ) cil managed 
        {
            .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
                01 00 00 00
            )
            // Method begins at RVA 0x2449
            // Code size 8 (0x8)
            .maxstack 8
    
            IL_0000: ldarg.0
            IL_0001: ldarg.1
            IL_0002: stfld string LearningAttribute.StudentVIP::'<VIPGroup>k__BackingField'
            IL_0007: ret
        } // end of method StudentVIP::set_VIPGroup
    
        .method public hidebysig 
            instance void HomeWork () cil managed 
        {
            .custom instance void LearningAttribute.CustomAttribute::.ctor(int32) = (
                01 00 e4 07 00 00 00 00
            )
            // Method begins at RVA 0x2452
            // Code size 13 (0xd)
            .maxstack 8
    
            IL_0000: nop
            IL_0001: ldstr "Homework"
            IL_0006: call void [mscorlib]System.Console::WriteLine(string)
            IL_000b: nop
            IL_000c: ret
        } // end of method StudentVIP::HomeWork
    
        .method public hidebysig specialname 
            instance int64 get_Salary () cil managed 
        {
            .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
                01 00 00 00
            )
            // Method begins at RVA 0x2460
            // Code size 7 (0x7)
            .maxstack 8
    
            IL_0000: ldarg.0
            IL_0001: ldfld int64 LearningAttribute.StudentVIP::'<Salary>k__BackingField'
            IL_0006: ret
        } // end of method StudentVIP::get_Salary
    
        .method public hidebysig specialname 
            instance void set_Salary (
                int64 'value'
            ) cil managed 
        {
            .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
                01 00 00 00
            )
            // Method begins at RVA 0x2468
            // Code size 8 (0x8)
            .maxstack 8
    
            IL_0000: ldarg.0
            IL_0001: ldarg.1
            IL_0002: stfld int64 LearningAttribute.StudentVIP::'<Salary>k__BackingField'
            IL_0007: ret
        } // end of method StudentVIP::set_Salary
    
        .method public hidebysig specialname rtspecialname 
            instance void .ctor () cil managed 
        {
            // Method begins at RVA 0x2471
            // Code size 8 (0x8)
            .maxstack 8
    
            IL_0000: ldarg.0
            IL_0001: call instance void LearningAttribute.Student::.ctor()
            IL_0006: nop
            IL_0007: ret
        } // end of method StudentVIP::.ctor
    
        // Properties
        .property instance string VIPGroup()
        {
            .custom instance void LearningAttribute.CustomAttribute::.ctor(int32) = (
                01 00 e3 07 00 00 00 00
            )
            .get instance string LearningAttribute.StudentVIP::get_VIPGroup()
            .set instance void LearningAttribute.StudentVIP::set_VIPGroup(string)
        }
        .property instance int64 Salary()
        {
            .get instance int64 LearningAttribute.StudentVIP::get_Salary()
            .set instance void LearningAttribute.StudentVIP::set_Salary(int64)
        }
    
    } 

    第三步:使用特性类实例。

    将特性与程序实体相关联后,利用反射来获取附加在这些Model上的数据。一般是传入这个实体,然后利用反射判断其是否贴有数据,如果有,然后调用object[] aAttributeArray = type.GetCustomAttributes(typeof(CustomAttribute), true);拿到这个特性的对象(其在这个环节进行了实例化),同时此时type又包含具体的数据信息,就可以将特性的附加信息与type进行互操作了。

                    Student student = new Student();
                    StudentVIP studentVIP = new StudentVIP()
                    {
                        Id = "1",
                        Name = "HAHAH",
                        VIPGroup = "Super学员"
    
                    };
                    InvokeCenter.ManagerStudent<Student>(studentVIP);
        public class InvokeCenter
        {
            public static void ManagerStudent<T>(T student) where T : Student
            {
                Console.WriteLine(student.Id);
                Console.WriteLine(student.Name);
                student.Answer("LNW");
    
                Type type = student.GetType();
                //这个type类级别上标注了特性
                if (type.IsDefined(typeof(CustomAttribute), true))//先判断
                {
                    object[] aAttributeArray = type.GetCustomAttributes(typeof(CustomAttribute), true);//此处为type
                    foreach (CustomAttribute item in aAttributeArray)
                    {
                        item.Show(type.Name);
                    
                    }
    
                }
                //这个type属性上标注了特性
                foreach (var prop in type.GetProperties())//先判断
                {
                    if (prop.IsDefined(typeof(CustomAttribute), true))
                    {
                        object[] aAttributeArray = prop.GetCustomAttributes(typeof(CustomAttribute), true);//此处为prop
                        foreach (CustomAttribute item in aAttributeArray)
                        {
                            item.Show(prop.Name);
                        }
                    }
                }
                //同理,方法上也可以获取
                foreach (var method in type.GetMethods())
                {
                    if (method.IsDefined(typeof(CustomAttribute), true))
                    {
                        object[] aMethodArray = method.GetCustomAttributes(typeof(CustomAttribute), true);//此处为method
                        foreach (CustomAttribute item in aMethodArray)
                        {
                            item.Show(method.Name);
                        }
                    }
                }
            }
        }

    结果为:

     使用场景2

    添加说明信息并获取,方便进行拓展。

    比如我想根据不同的枚举状态显示不同的字符串信息。

    定义枚举:

        public enum UserStatus
        {
            /// <summary>
            /// 正常状态
            /// </summary>
            Normal=0,
            /// <summary>
            /// 冻结状态
            /// </summary>
            Frozen =1,
            /// <summary>
            /// 删除状态
            /// </summary>
            Delted = 2
    
        }

    常规判断操作:

                    UserStatus userStatus = UserStatus.Normal;
                    if (userStatus == UserStatus.Normal)
                    {
                        Console.WriteLine("正常状态");
                    }
                    else if (userStatus == UserStatus.Frozen)
                    {
                        Console.WriteLine("冻结状态");
                    }
                    else
                    {
                        Console.WriteLine("已删除");
                    }
    //.....如果发生文字修改,那么改动量特别大,if else if 分支特别长。。

    使用特性

        public enum UserStatus
        {
            /// <summary>
            /// 正常状态
            /// </summary>
            [Remark("正常状态")]
            Normal=0,
            /// <summary>
            /// 冻结状态
            /// </summary>
            [Remark("冻结状态")]
            Frozen =1,
            /// <summary>
            /// 删除状态
            /// </summary>
            [Remark("删除状态")]
            Delted = 2
    //可以自由拓展。
    }
    //拓展方法
                    string remark3withExtendMethod = UserStatus.Delted.GetRemark();

    public static class AttributeExtend
    {

     public static string GetRemark(this Enum value)//对比上面同方法

            {
                Type type = value.GetType();
                var field = type.GetField(value.ToString());//
                if (field.IsDefined(typeof(RemarkAttribute), true))
                {
                    RemarkAttribute attribute = field.GetCustomAttribute(typeof(RemarkAttribute), true) as RemarkAttribute;
                    return attribute.Remak;
                }
                else
                {
                    return value.ToString();
                }
            }
    }
    //其中GetFiled API含义如下:
    // 摘要:
            //     搜索具有指定名称的公共字段。
            //
            // 参数:
            //   name:
            //     包含要获取的数据字段的名称的字符串。
            //
            // 返回结果:
            //     如找到,则为表示具有指定名称的公共字段的对象;否则为 null。
            //
            // 异常:
            //   T:System.ArgumentNullException:
            //     name 为 null。
            //
            //   T:System.NotSupportedException:
            //     此 System.Type 对象是尚未调用其 System.Reflection.Emit.TypeBuilder.CreateType 方法的 System.Reflection.Emit.TypeBuilder。
            public FieldInfo GetField(string name);

    使用场景3

    做数据验证。

    public class IntValidateAttribute : Attribute//特性名称约定俗成是以Attribute结尾,特性命名以具体功能名称为命名,此处就是整型数据验证
        {
            /// <summary>
            /// 最小值
            /// </summary>
            private int minValue { get; set; }
            /// <summary>
            /// 最大值
            /// </summary>
            private int maxValue { get; set; }
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="minValue"></param>
            /// <param name="maxValue"></param>
            public IntValidateAttribute(int minValue, int maxValue)
            {
                this.minValue = minValue;
                this.maxValue = maxValue;
            }
            /// <summary>
            /// 检验值是否合法
            /// </summary>
            /// <param name="checkValue"></param>
            /// <returns></returns>
            public bool Validate(int checkValue)
            {
                return checkValue >= minValue && checkValue <= maxValue;
            }
        }
    
         public class User
        {
            [IntValidate(1, 10)]
            public int Id { get; set; }
            public string Name { get; set; }
        }
    
        public class BaseDal
        {
            public static string Insert<T>(T model)
            {
                Type modelType = typeof(T);//Model模型
                //获取类型的所有属性
                PropertyInfo[] propertyInfos = modelType.GetProperties();
                
                bool boIsCheck = true;
                //循环所有属性
                foreach (var property in propertyInfos)
                {
                    //获取属性的所有特性
                    object[] attrs = property.GetCustomAttributes(true);
                    if (property.PropertyType.Name.ToLower().Contains("int"))
                    {
                        foreach (var attr in attrs)
                        {
                            if (attr is IntValidateAttribute)
                            {
                                IntValidateAttribute intValidate = (IntValidateAttribute)attr;//拿到追加在Model上的特性实例化对象
                                //执行特性的验证逻辑
                                boIsCheck = intValidate.Validate((int)property.GetValue(model));//特性实例化对象intValidate执行对象方法Validate,同时proprty.GetValue(model)获取此时传进来的model实体的属性数据。进行数据验证。
                            }
                        }
                    }
                    if (!boIsCheck)
                    {
                        break;
                    }
                }
                if (boIsCheck)
                {
                    return "验证通过,插入数据库成功";
                }
                else
                {
                    return "验证失败";
                }
            }
        }
         class Program
        {
            public static void Main(string[] args)
            {
                string msg = BaseDal.Insert<User>(new User() { Id = 123, Name = "lvcc" });//传入Model User和User实体对象new User() { Id = 123, Name = "lvcc" }
                Console.WriteLine(msg);
    }
    }

    总结:

    使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。 将特性与程序实体相关联后,可以在运行时使用反射这项技术查询特性。 

    特性具有以下属性:

    • 特性向程序添加元数据。 元数据是程序中定义的类型的相关信息。 所有 .NET 程序集都包含一组指定的元数据,用于描述程序集中定义的类型和类型成员。 可以添加自定义特性来指定所需的其他任何信息。 
    • 可以将一个或多个特性应用于整个程序集、模块或较小的程序元素(如类和属性)。
    • 特性可以像方法和属性一样接受自变量。
    • 程序可使用反射来检查自己的元数据或其他程序中的元数据。 有关详细信息。

    同时在MVC---EF--WCF--IOC 都有使用特性,无处不在。

     参考资料:

    https://www.cnblogs.com/chenxizhaolu/p/9497768.html

    https://www.cnblogs.com/woadmin/p/9406970.html

     
  • 相关阅读:
    MySql创建库 Challenge
    未能启用约束。一行或多行中包含违反非空、唯一或外键约束的值的解决办法.
    小总结:用反射机制创建的分配数据分配器
    工厂模式的反思
    单机安装“完整”SharePoint 2010
    作业调度框架 Quartz.NET 2.0 StepByStep(2)
    UI线程同步
    每日见闻(一)
    作业调度框架 Quartz.NET 2.0 StepByStep
    基础算法(ACwing)
  • 原文地址:https://www.cnblogs.com/shuzhongke/p/14008171.html
Copyright © 2011-2022 走看看