C#–特性详解

一、特性是什么

1、特性定义

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

2、特性的语法

特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。

1
2
[attribute(positional_parameters, name_parameter = value, ...)]
element

3、特性和注释有什么区别

特性很厉害,加了特性之后,就有很厉害的功能
[Obsolete]编译时就有提示,影响了编译器[Obsolete(“请不要使用这个了,请使用什么来代替”, true)]甚至导致编译报错
[Serializable]对象就可以序列化,影响了程序运行


using System;

namespace MyAttribute
{
    /// <summary>
    /// 这里是注释,除了让人看懂这里写的是什么,对运行没有任何影响
    /// </summary>
    ///[Obsolete("请不要使用这个了,请使用什么来代替")]//对编译都产生了影响,编译出现警告
    ///[Obsolete("请不要使用这个了,请使用什么来代替", true)]//对编译都产生了影响,编译报错不通过
    [Serializable]//可以序列化和反序列化
    public class Student
    {        
        public int Id { get; set; }
        
        public string Name { get; set; }
        
        public void Study()
        {
            Console.WriteLine($"这里是{this.Name}在学习");
        }
        
        public string Answer([Custom]string name)
        {
            return $"This is {name}";
        }
    }
}

特性无处不在:EF–MVC–WCF–WebService–UnitTest–IOC–AOP–SuperSocket

二、特性声明和使用

1、什么是特性

特性其实就是一个类,直接或间接继承自Attribute


#region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\mscorlib.dll
// Decompiled with ICSharpCode.Decompiler 6.1.0.5902
#endregion

using System.Reflection;
using System.Runtime.InteropServices;

namespace System
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
    [ComVisible(true)]
    public sealed class SerializableAttribute : Attribute
    {
        internal static Attribute GetCustomAttribute(RuntimeType type)
        {
            if ((type.Attributes & TypeAttributes.Serializable) != TypeAttributes.Serializable)
            {
                return null;
            }

            return new SerializableAttribute();
        }

        internal static bool IsDefined(RuntimeType type)
        {
            return type.IsSerializable;
        }
    }
}
#if false // 反编译日志
缓存中的 9 项
#endif

2、自定义一个特性


using System;

namespace MyAttribute
{    
    public class CustomAttribute : Attribute
    {
        
    }
}

约定俗成用Attribute结尾,标记时就可以省略掉;可以用中括号包裹,然后标记到元素,其实就是调用构造函数;


using System;

namespace MyAttribute
{
    [Custom]
    public class Student
    {
        [Custom]
        public int Id { get; set; }
        public string Name { get; set; }
        [Custom]
        public void Study()
        {
            Console.WriteLine($"这里是{this.Name}跟着Gerry老师学习");
        }
    }
}

3、AttributeUsage特性

直接在一个元素上添加多个相同的特性,会报错特性重复,需要在特性上面添加特性标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]这样就可以给同一个元素添加多个相同的特性了


using System;

namespace MyAttribute
{
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
    public class CustomAttribute : Attribute
    {
        public CustomAttribute()
        {
            Console.WriteLine($"{this.GetType().Name} 无参数构造函数执行");
        }
        public CustomAttribute(int id)
        {
            Console.WriteLine($"{this.GetType().Name} int参数构造函数执行");
            this._Id = id;
        }
        public CustomAttribute(string name)
        {
            Console.WriteLine($"{this.GetType().Name} string参数构造函数执行");
            this._Name = name;
        }
    }
}

多个相同的特性情况展示


using System;

namespace MyAttribute
{    
    [Custom]
    [Custom()]    
    [Custom(10)]    
    public class Student
    {
        [Custom]
        public int Id { get; set; }
        public string Name { get; set; }
        [Custom]
        public void Study()
        {
            Console.WriteLine($"这里是{this.Name}跟着Gerry老师学习");
        }
       
        [Custom(0)]
        public string Answer([Custom]string name)
        {
            return $"This is {name}";
        }
    }
}

AttributeUsage特性,影响编译器运行,指定修饰的对象、能否重复修饰、修饰的特性子类是否生效,建议是明确约束用在哪些对象的

[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class|AttributeTargets.Property, AllowMultiple = true)]

4、特性可以指定属性和字段


using System;

namespace MyAttribute
{

    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
    public class CustomAttribute : Attribute
    {
        public CustomAttribute()
        {
            Console.WriteLine($"{this.GetType().Name} 无参数构造函数执行");
        }
        public CustomAttribute(int id)
        {
            Console.WriteLine($"{this.GetType().Name} int参数构造函数执行");
            this._Id = id;
        }
        public CustomAttribute(string name)
        {
            Console.WriteLine($"{this.GetType().Name} string参数构造函数执行");
            this._Name = name;
        }

        private int _Id = 0;
        private string _Name = null;

        public string Remark;
        public string Description { get; set; }
    }
}

using System;

namespace MyAttribute
{
    [Custom(Remark = "123")]
    [Custom(Remark = "123", Description = "456")]
    [Custom(0, Remark = "123")]
    [Custom(0, Remark = "123", Description = "456")]
    public class Student
    {        
        public int Id { get; set; }
        public string Name { get; set; }
        
        public void Study()
        {
            Console.WriteLine($"这里是{this.Name}跟着Gerry老师学习");
        }        
    }
}

5、特性还可以修饰返回值和参数


using System;

namespace MyAttribute
{
    public class Student
    {
        [return: Custom]
        public string Answer([Custom]string name)
        {
            return $"This is {name}";
        }
    }
}

6、多重修饰既可以中括号隔开,也可以一个中括号里面逗号隔开


using System;

namespace MyAttribute
{
    [Custom]
    [Custom()]
    [Custom(Remark = "123")]
    [Custom(Remark = "123", Description = "456")]
    [Custom(0)]
    [Custom(0, Remark = "123")]
    [Custom(0, Remark = "123", Description = "456")]
    public class Student
    {
        [return: Custom, Custom,Custom(), Custom(0, Remark = "123", Description = "456")]        
        public string Answer(string name)
        {
            return $"This is {name}";
        }
    }
}

四、特性应用案例

1、特性实现枚举展示描述信息

(1)创建枚举类


namespace MyAttribute.EnumExtend
{
    /// <summary>
    /// 用户状态
    /// </summary>
    public enum UserState
    {
        /// <summary>
        /// 正常状态
        /// </summary>
        [Remark("正常状态")]
        Normal = 0,
        /// <summary>
        /// 已冻结
        /// </summary>
        [Remark("已冻结")]
        Frozen = 1,
        /// <summary>
        /// 已删除
        /// </summary>
        [Remark("已删除")]
        Deleted = 2
    }
}

(2)创建特性类


using System;

namespace MyAttribute.EnumExtend
{
    /// <summary>
    /// Remark特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Field)]
    public class RemarkAttribute : Attribute
    {
        public string Remark { get; private set; }
        public RemarkAttribute(string remark)
        {
            this.Remark = remark;
        }
    }
}

(3)枚举扩展方法


using System;
using System.Reflection;

namespace MyAttribute.EnumExtend
{
    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 = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true);
                return attribute.Remark;
            }
            else
            {
                return value.ToString();
            }
        }
    }
}

2、特性实现数据验证

(1)基类抽象特性


using System;

namespace MyAttribute.ValidateExtend
{
    public abstract class AbstractValidateAttribute : Attribute
    {
        public abstract bool Validate(object oValue);
    }
}

(2)子类特性实现–数字长度


using System;

namespace MyAttribute.ValidateExtend
{
    [AttributeUsage(AttributeTargets.Property)]
    public class LongAttribute : AbstractValidateAttribute
    {
        private long _Min = 0;
        private long _Max = 4;
        public LongAttribute(long min, long max)
        {
            this._Min = min;
            this._Max = max;
        }

        public override bool Validate(object oValue)
        {
            return oValue != null
                && long.TryParse(oValue.ToString(), out long lValue)
                && lValue >= this._Min
                && lValue <= this._Max;
        }
    }
}

(3)子类特性实现–可空


namespace MyAttribute.ValidateExtend
{
    public class RequiredAttribute : AbstractValidateAttribute
    {
        public override bool Validate(object oValue)
        {
            return oValue != null
                && !string.IsNullOrWhiteSpace(oValue.ToString());
        }
    }
}

(4)子类特性实现–字符串长度


using System;

namespace MyAttribute.ValidateExtend
{
    [AttributeUsage(AttributeTargets.Property)]
    public class StringLengthAttribute : AbstractValidateAttribute
    {
        private int _Min = 0;
        private int _Max = 3;
        public StringLengthAttribute(int min, int max)
        {
            this._Min = min;
            this._Max = max;
        }

        public override bool Validate(object oValue)
        {
            return oValue != null
                && oValue.ToString().Length >= this._Min
                && oValue.ToString().Length <= this._Max;
        }
    }
}

(5)泛型扩展方法


using System;

namespace MyAttribute.ValidateExtend
{
    public static class AttributeExtend
    {
        public static bool Validate<T>(this T t)
        {
            Type type = t.GetType();
            foreach (var prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
                {
                    object oValue = prop.GetValue(t);
                    foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
                    {
                        if (!attribute.Validate(oValue))
                            return false;
                    }
                }
            }
            return true;
        }
    }
}

(6)常规类字段定义


using System;

namespace MyAttribute.ValidateExtend
{
    public static class AttributeExtend
    {
        public static bool Validate<T>(this T t)
        {
            Type type = t.GetType();
            foreach (var prop in type.GetProperties())
            {
                if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
                {
                    object oValue = prop.GetValue(t);
                    foreach (AbstractValidateAttribute attribute in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))
                    {
                        if (!attribute.Validate(oValue))
                            return false;
                    }
                }
            }
            return tue;
        }
    }
}

(7)类调用扩展方法验证字段

using MyAttribute.EnumExtend;
using MyAttribute.ValidateExtend;
using System;

namespace MyAttribute
{
    /// <summary>
    /// main方法调用
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {                
                #region 特性实现数据验证,并且可扩展
                {
                    //通过特性去提供额外行为
                    //数据验证--到处都需要验证
                    StudentVip student = new StudentVip()
                    {
                        Id = 123,
                        Name = "无为",
                        QQ = 729220650,
                        Salary = 1010000
                    };                    

                    if (student.Validate())
                    {
                        Console.WriteLine("特性校验成功");
                    }
                    //1 可以校验多个属性
                    //2 支持多重校验
                    //3 支持规则的随意扩展
                }
                #endregion
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
    }
}
posted @ 2024-04-05 17:31  一码事  阅读(530)  评论(0编辑  收藏  举报