手记

C# 特性[Attribute]

开篇语

本文开始之前,首先我想问下大家对于属性和特性知道多少?属性和特性又有何区别?

对于该单词,我更想把它称之为:特性。对于属性和特性就是名称上有纠葛(不知道你们迷不迷,反正我写本文之前我是迷了),什么是属性?属性是面向对象编程的基本概念,提供了对私有字段的访问封装,在C#中以get和set访问器方法实现对可读可写属性的操作,提供了安全和灵活的数据访问封装。什么是特性?下面内容就说明下:

介绍

使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。将特性与程序实体相关联后,可以在运行时使用 反射 这项技术查询特性。详情 用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

简单总结:定制特性attribute,本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。

常用特性

AttributeUsage

AttributeUsage特性用于控制如何应用自定义特性到目标元素,有三个数据属性可用以修饰我们的自定义的属性

    [AttributeUsage(validOn: AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public class HelpAttribute : Attribute
    {

    }

表示该特定只能标识在类上,并且同一个类上只能用一个属性修饰,并且自定义属性的修饰不能由修饰类的派生类继承。

Flags

以Flags特性来将枚举数值看作位标记,而非单独的数值,例如我有如下的一个需求,当我想要取得用户信息的时候,会先从本地缓存中查找,找不到然后从分布式缓存中查找,最后找不到再从数据库中查询。但是有些场景我又不需要查询数据库。

所以会建立下面的这种模型

public UserEntity  GetUserInfo(List<DataSource>  dataSources)
{
    var xxxx = new UserEntity();
    if(dataSources.Any(DataSource.Local)
    {
        //从本地缓存中获取
        return xxxx;
    }
 
    if(dataSources.Any(DataSource.Distribution)
    {
        //从分布式缓存中获取
        //更新本地缓存
        return xxxx;
    }
 
    if(dataSources.Any(DataSource.DB)
    {
        //从DB中获取
        //更新分布式缓存
        //更新本地缓存
    }
    return xxxx;
}

但是每次调用者都去构建一个List,比较麻烦,此时我们可以使用枚举中的Flags特性,修改程序如下:

首先是枚举的定义上,要加上 [Flags] 特性标签,并且定义 一般都是 2的n次方,主要是便于位移运算

/// <summary>
///数据取得地方
/// </summary>
[Flags]
public enum DataSource
{
    /// <summary>
    ///本地缓存
    /// </summary>
    [Description("本地缓存")]
    LocalCache = 1,
 
    /// <summary>
    ///分布式缓存
    /// </summary>
    [Description("分布式缓存")]
    DistributeCache = 2,
 
    /// <summary>
    ///数据库
    /// </summary>
    [Description("数据库")]
    DB = 4,
}

修改代码

public UserEntity  GetUserInfo(DataSource dataSources)
{
    var xxxx = new UserEntity();
    if(dataSources.HasFlags(DataSource.Local)
    {
        //从本地缓存中获取
        return xxxx;
    }
    if(dataSources.HasFlags(DataSource.Distribution)
    {
        //从分布式缓存中获取
        //更新本地缓存
        return xxxx;
    }
    if(dataSources.HasFlags(DataSource.DB)
    {
        //从DB中获取
        //更新分布式缓存
        //更新本地缓存
    }
    return xxxx;
}

调用的地方,可以用过“|”来指定,例如我只想用分布式缓存和数据库,那么:

var userInfo = GetUserInfo(DataSource.Distribution | DataSource.DB);

DllImport

DllImport特性,可以让我们调用非托管代码,所以我们可以使用DllImport特性引入对Win32 API函数的调用

[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();

Serializable

Serializable特性表明了应用的元素可以被序列化(serializated)

[Serializable]
public class SampleClass
{
    // Objects of this type can be serialized.
}

Conditional

Conditional特性,用于条件编译,在调试时使用。注意:Conditional不可应用于数据成员和属性。

自定义特性

可通过定义特性类创建自己的自定义特性,特性类是直接或间接派生自 Attribute 的类,可快速轻松地识别元数据中的特性定义。假设我们希望使用编写类的程序员名字来标记该类,那么我们就需要自定义一个Author特性类

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
    public class AuthorAttribute : Attribute
    {
        public string AuthorName;
        public double version;

        public AuthorAttribute(string authorName)
        {
            this.AuthorName = authorName;
            version = 1.0;
        }
    }

类名 AuthorAttribute 是该特性的名称,即 Author 加上 Attribute 后缀。由于该类继承自 System.Attribute,因此它是一个自定义特性类。构造函数的参数是自定义特性的位置参数。在此示例中,name 是位置参数。所有公共读写字段或属性都是命名参数。在本例中,version 是唯一的命名参数。

请注意,使用 AttributeUsage 特性可使 Author 特性仅对类和 struct 声明有效。

可按照下面的方式使用特性

    [Author("张三", version = 1.1)]
    [Author("李四", version = 1.2)]
    public class SampleClass
    {
        // 业务逻辑代码
    }

获取自定义参数

var attr = typeof(SampleClass).GetCustomAttributes(typeof(AuthorAttribute), true);

GetCustomAttributes 会以数组形式返回 Author 对象和任何其他特性对象

参考文档

0人推荐
随时随地看视频
慕课网APP