C# 特性学习

191 阅读4分钟

C# 特性

定义 :特性是一种类,继承自Attribute类,可以用于修饰类、方法、属性、字段、参数等,用于为它们添加元数据,以便在运行时通过反射获取它们。

命名规范:以Attribute结尾,如SerializableAttribute。

分类:内置特性、自定义特性。

创建特性

  1. 定义特性类,继承自Attribute类
  2. 标记,使用反射获取特性
  3. 调用

简单的例子


// 定义特性,继承自Attribute类
class MyAttribute : Attribute
{
    public MyAttribute(string showInfo) {
        this.ShowInfo = showInfo;
    }

    public string ShowInfo { get; set; }
}

// 使用特性
[My("这是一个特性")]
class UseAttribute
{
    // 通过反射调用特性
    public void Test01( ) {
        Type type = typeof(UseAttribute);
        object[] objs = type.GetCustomAttributes(true);
        foreach (object obj in objs) {
            MyAttribute? myAttribute = obj as MyAttribute;
            if (myAttribute != null) {
                Console.WriteLine(myAttribute.ShowInfo);
            }
        }
    }
}

class Program {

    static void Main(string[] args) {
        UseAttribute useAttribute = new UseAttribute();
        useAttribute.Test01();
    }
}

简单的场景1

定义一个枚举,然后给枚举的每个值添加一个特性,然后使用反射获取特性,就可以获取到枚举的值的特性了。

enum UserState
{
    /// <summary>
    /// 正常
    /// </summary>
    [Remark("正常")]
    Normal = 0,
    /// <summary>
    /// 冻结
    /// </summary>
    [Remark("冻结")]
    Frozen = 1,
    /// <summary>
    /// 删除
    /// </summary>
    [Remark("删除")]
    Deleted = 2

}

使用反射获取特性


class AttributeInvoke
{
    /// <summary>
    /// 获取枚举的特性
    /// </summary>
    public string GetRemark(UserState userState)
    {
        Type type = userState.GetType();
        // 获取枚举的字段
        var field = type.GetField(userState.ToString());

        // 获取字段上的特性
        if (field?.IsDefined(typeof(RemarkAttribute), true) == true)
        {
            if (field.GetCustomAttribute(
                typeof(RemarkAttribute), true) is RemarkAttribute remarkAttribute)
            {
                return remarkAttribute.Remark;
            }
        }

        return userState.ToString();
    }
}

调用测试

class Program
{

    static void Main(string[] args)
    {
        // UseAttribute useAttribute = new UseAttribute();
        // useAttribute.Test01();

        // 场景1:
        UserState userState = UserState.Frozen;
        // if 条件判断写法
        // if (userState == UserState.Normal)
        // {
        //     Console.WriteLine("正常");
        // }
        // else if (userState == UserState.Frozen)
        // {
        //     Console.WriteLine("冻结");
        // }
        // else if (userState == UserState.Deleted)
        // {
        //     Console.WriteLine("删除");
        // }
        // 这样写很麻烦,且改动不方便

        // 使用特性加反射
        AttributeInvoke attributeInvoke = new();
        Console.WriteLine(attributeInvoke.GetRemark(userState));
    }
}

场景2

[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class ShowAttribute : Attribute
{
    public ShowAttribute(string showInfo)
    {
        ShowInfo = showInfo;
    }

    public void Show()
    {
        Console.WriteLine(ShowInfo);
    }

    public string ShowInfo { get; private set; }
}

定义一个类,然后在类上、方法上、属性上、字段上使用特性


[Show("我是在类上的特性")]
[Show("我是在类上的特性2")]
public class ShowTest
{
    [Show("我是在方法上的特性")]
    public void Test()
    {

    }

    [Show("我是在属性上的特性")]
    public string TestProperty { get; set; }

    [Show("我是在字段上的特性")]
    public string TestField;
}

然后使用反射获取特性,就可以获取到类上、方法上、属性上、字段上的特性了


public static class InvokeConter
{
    public static void InvonkeManager<T>(this T showTest) where T : new()
    {
        Type type = showTest.GetType();
        if (type.IsDefined(typeof(ShowAttribute), true))
        {
            // 获取类上的特性
            object[] attribues = type.GetCustomAttributes(typeof(ShowAttribute), true);
            foreach (ShowAttribute attribue in attribues)
            {
                attribue.Show();
            }

            // 获取方法上的特性
            foreach (var method in type.GetMethods())
            {
                if (method.IsDefined(typeof(ShowAttribute), true))
                {
                    object[] attribues1 = method.GetCustomAttributes(typeof(ShowAttribute), true);
                    foreach (ShowAttribute attribue in attribues1)
                    {
                        attribue.Show();
                    }
                }
            }

            // 获取属性上的特性
            foreach (var property in type.GetProperties())
            {
                if (property.IsDefined(typeof(ShowAttribute), true))
                {
                    object[] attribues1 = property.GetCustomAttributes(typeof(ShowAttribute), true);
                    foreach (ShowAttribute attribue in attribues1)
                    {
                        attribue.Show();
                    }
                }
            }

            // 获取字段上的特性
            foreach (var field in type.GetFields())
            {
                if (field.IsDefined(typeof(ShowAttribute), true))
                {
                    object[] attribues1 = field.GetCustomAttributes(typeof(ShowAttribute), true);
                    foreach (ShowAttribute attribue in attribues1)
                    {
                        attribue.Show();
                    }
                }
            }
        }
    }
}

测试


class Test
{

    static void Main(string[] args)
    {
        // 普通调用方式
        // InvokeConter invokeConter = new();
        // invokeConter.InvonkeManager(new ShowTest());
        // 扩展方法
        ShowTest showTest = new();
        showTest.InvonkeManager();
    }
}

特性实现验证数据

定义一个抽象类,继承自Attribute类,实现一个抽象方法,用于验证数据,然后在需要验证的属性上使用特性,然后在需要验证的时候调用Validate方法,就可以验证数据了。

public abstract class AbstractValidateAttribute : Attribute
{
    public abstract bool Validate(object objValue);
}

定义几个特性,继承自抽象类,实现抽象方法,用于验证数据。

/// <summary>
/// 验证是否为空
/// </summary>
public class RequiredAttribute : AbstractValidateAttribute
{
    public override bool Validate(object objValue)
    {
        return objValue != null && !string.IsNullOrWhiteSpace(objValue.ToString());
    }
}

/// <summary>
/// 验证字符串长度
/// </summary>
public class StringLengthAttribute : AbstractValidateAttribute
{
    private int _Min = 0;
    private int _Max = 0;

    public StringLengthAttribute(int min, int max)
    {
        this._Min = min;
        this._Max = max;
    }

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

/// <summary>
/// 验证数字长度
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class LongAttribute : AbstractValidateAttribute
{
    private long _Long = 0;
    public LongAttribute(long LongLength)
    {
        this._Long = LongLength;
    }

    public override bool Validate(object objValue)
    {
        return objValue?.ToString()?.Length <= this._Long;
    }
}

定义扩展方法,用于使用反射调用验证数据特性

public static class AttributeExtend
{
    public static bool Validate<T>(this T t)
    {
        Type type = t.GetType();
        // 获取类上的特性
        foreach (var property in type.GetProperties())
        {
            if (property.IsDefined(typeof(AbstractValidateAttribute), true))
            {
                // 获取属性上的特性
                object objValue = property.GetValue(t);
                foreach (AbstractValidateAttribute attribute in property.GetCustomAttributes(true))
                {
                    if (!attribute.Validate(objValue))
                    {
                        return false;
                    }   
                }
            }
        }
        return true;
    }
}

测试

public class Student
{
    public int Id { get; set; }
    [Required] // 验证是否为空
    [StringLength(5, 10)] // 验证字符串长度
    public string Name { get; set; }
    [Required]
    [Long(11)]
    public long Phone { get; set; }
}

class Test
{

    static void Main(string[] args)
    {
        Student student = new()
        {
            Id = 1,
            Name = "张三asd",
            Phone = 12345678901
        };

        if (student.Validate()) 
        {
            Console.WriteLine("验证通过");
        }
        else
        {
            Console.WriteLine("验证失败");
        }
    }
}

学习自视频:C#进阶学习之特性