Attribute

      • 1.什么是特性
      • 2.应用特性
      • 3.预定义的保留的特性
        • 3.1 Obsolete特性
        • 3.2 Conditional特性
        • 3.3 调用者信息特性
        • 3.4 DebuggerStepThrough特性
      • 4.特性的其他内容
        • 4.1 多个特性
        • 4.2 其他类型的目标
        • 4.3 全局特性
      • 5.自定义特性
        • 5.1 声明自定义特性
        • 5.2 使用特性的构造函数
        • 5.3 指定构造函数
        • 5.4 构造函数中的位置参数和命名参数
        • 5.5 限制特性的使用
      • 6.访问特性
        • 6.1 使用IsDefined方法
        • 6.2 使用GetCustomAttributes方法

1.什么是特性

特性(attribute)是一种允许向程序的程序集增加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类。

  • 将应用了特性的程序结构叫做目标
  • 设计用来获取和使用元数据的程序叫做特性的消费者
    image

2.应用特性

特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集,可以通过把特性应用到结构来实现。

  • 在结构前放置特性片段来应用特性
  • 特性片段被方括号包围,其中是特性名和特性的参数列表
[ Serializable ] // 特性
public class MyClass {...} 
[ MyAttribute("Simple Class", "Version 0.0.1") ] // 带有参数的特性
public class MyOtherClass {...}

3.预定义的保留的特性

3.1 Obsolete特性

可以使用Obsolete特性将程序结构标注为过期,并在代码编译时显示有用的警告信息。

class Program
{
    [Obsolete("Use new method!")]
    static void OldPrint()
    {
        Console.WriteLine("hello");
    }
    static void Main()
    {
        OldPrint();
        Console.ReadKey();
    }
}

即使OldPrint被标注为过期,Main方法还是会调用它, output

hello

但是在编译过程中,编译器生成警告信息 waring

Warning	CS0618	'Program.OldPrint()' is obsolete: 'Use new method!'	ConsoleApp1	C:\Users\kylew\Desktop\ConsoleApp1\ConsoleApp1\Program.cs	12	Active

另外一个Obsolete特性的重载接受了bool类型的第二个参数,用来指定目标是否应该被标记为错误而不仅仅是警告。

[Obsolete("Use new method!", true)] // 标记为错误

3.2 Conditional特性

Conditional特性允许包括或排斥特定方法的所有调用。

#define DoTrace
using System;
using System.Diagnostics;

class Program
{
    [Conditional("DoTrace")]
    static void Print1()
    {
        Console.WriteLine("1111");
    }
    static void Print2()
    {
        Console.WriteLine("2222");
    }
    static void Main()
    {
        Print1();
        Print2();
        Console.ReadKey();
    }
}

output

1111
2222

如果注释掉第一行来取消DoTrace的定义,
output

2222

3.3 调用者信息特性

调用者信息特性可以访问文件路径、代码行数、调用成员的名称等源代码信息。

  • 这三个特性为CallerFilePath、CallerLineNumber、CallerMenberName
  • 这些特性只能用于方法中的可选参数
using System;
using System.Runtime.CompilerServices;

class Program
{
    public static void MyTrace(string message,
        [CallerFilePath] string fileName = "",
        [CallerLineNumber] int lineNumber = 0,
        [CallerMemberName] string callingMenber = "")
    {
        Console.WriteLine($"File:{fileName}");
        Console.WriteLine($"Line:{lineNumber}");
        Console.WriteLine($"Called From:{callingMenber}");
        Console.WriteLine($"Message:{message}");
    }
    public static void Main()
    {
        MyTrace("simple message");
        Console.ReadKey();
    }
}

output

File:C:\Users\kylew\Desktop\ConsoleApp1\ConsoleApp1\Program.cs
Line:19
Called From:Main
Message:simple message

3.4 DebuggerStepThrough特性

在单步调试时,希望调试器不进入某些方法,仅仅执行该方法,然后继续调试下一行。

using System;
using System.Diagnostics;

class Program
{
    [DebuggerStepThrough]
    public static int Method() // 单步调试时不进入方法体,直接执行该方法
    {
        int sum = 0;
        for (int i = 0; i < 10; i++)
            sum += i;
        return sum;
    }
    public static void Main()
    {
        Console.WriteLine("start");
        Console.WriteLine($"sum = {Method()}");
        Console.ReadKey();
    }
}

4.特性的其他内容

4.1 多个特性

[ Serializable ] 
[ MyAttribute("simple class", "version 0.0.1") ] // 多层结构

[ MyAttribute("simple class", "version 0.0.1"), Serializable ] // 逗号分隔

4.2 其他类型的目标

[ MyAttribute("simple class", "version 0.0.1") ] // 字段上的特性
public int MyField;

[ Obsolete ] // 方法上的特性
[ MyAttribute("simple class", "version 0.0.1") ] 
public void Print() {...}

还可以显示的标注特性,从而将它用到特殊的目标结构,

[ method: MyAttribute("simple class", "version 0.0.1") ] 
[ return: MyAttribute("simple class", "version 0.0.1") ] 
public void Print() {...}

特性目标:

eventfieldmethodparam
method param property return
type typevar assembly module

4.3 全局特性

assembly和module用来将特性设置在程序集或模块级别:

  • 程序集级别的特性必须放置在任何命名空间之外,并且通常放置在AssemblyInfo.cs文件中
  • AssemblyInfo.cs文件通常包含有关公司、产品以及版权信息的元数据
[assembly: Assembly(MyTitle)]

5.自定义特性

5.1 声明自定义特性

  • 声明一个派生自System.Attribute的类
  • 特性名称以后缀Attribute结尾
  • 建议声明一个sealed的特性类
public sealed class MyAttributeAttribute: System.Attribute // 使用时可简写为MyAttribute
{...}

由于特效持有目标的信息,所有特性类的公共成员只能是:

  • 字段
  • 属性
  • 构造函数

5.2 使用特性的构造函数

每一个特性至少必须有一个公共构造函数,若不声明构造函数,编译器会产生一个隐式、公共且无参的构造函数。

public MyAttributeAttribute(string desc, string ver) 
{
    Description = desc;
    VersionNumber = ver;
}

5.3 指定构造函数

为目标应用特性时,实际上是在指定应该使用哪个构造函数。

[MyAttribute("hello")] // 使用一个字符串的构造函数
public int MyField;

[MyAttribute("hello", "world")] // 使用两个字符串的构造函数
public void MyMethod() {...}

[MyAttribute] // 无参时可省略括号 

5.4 构造函数中的位置参数和命名参数

[MyAttribute("hello", Reviewer = "Kyle", Ver = "0.0.1")]
            位置参数             命名参数      命名参数
public sealed class MyAttributeAttribute:System.Attribute
{
    public string Description;
    public string Ver;
    public string Reviewer;
    public MyAttributeAttribute(string desc)
    {
        Description = desc;
    }
}
[MyAttribute("hello", Reviewer = "Kyle", Ver = "0.0.1")]
class MyClass
{ ... }

5.5 限制特性的使用

使用AttributeUsage特性来限制特性使用在某个目标类型上。

[AttributeUsage(AttributeTargets.Method)]
public sealed class MyAttributeAttribute:System.Attribute
{ ... }

AttributeUsage有三个重要的公共属性:

名字意义默认值
ValidOn 保存特性能应用到目标类型的列表。构造函数的第一个参数必须是AttributeTarget类型的枚举值  
Inherited 一个布尔值,它指示特性是否会被装饰类型的派生类所继承 true
AllowMultiple 一个指示目标是否被应用多个特性的示例的布尔值 false
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)]
public sealed class MyAttributeAttribute:System.Attribute
{ ... }

AttributeTarget枚举成员:

AllAssemblyClassConstructor
Delegate Enum Event Field
GenericParameter Interface Method Module
Parameter Property RetrunValue Struct

一个AttributeUsage的具体用法:

[AttributeUsage(AttributeTargets.Class, // 只能应用到类上
                Inherited = false, // 不会被应用它的派生类所继承
                AllowMultiple = false)] // 不能有多个MyAttribute实例应用到同一目标上
public sealed class MyAttributeAttribute:System.Attribute
{ ... }

6.访问特性

6.1 使用IsDefined方法

使用Type对象的IsDefined方法来检测某个特性是否应用到了某个类上。

[AttributeUsage(AttributeTargets.Class,
    Inherited = false,
    AllowMultiple = false)]
public sealed class MyAttributeAttribute:System.Attribute
{
    public string Description;
    public string Ver;
    public string Reviewer;
    public MyAttributeAttribute(string desc)
    {
        Description = desc;
    }
}
[MyAttribute("hello", Reviewer = "Kyle", Ver = "0.0.1")]
class MyClass { }
class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Type t = mc.GetType();
        bool isDefined = t.IsDefined(typeof(MyAttributeAttribute), false);
        if (isDefined)
            Console.WriteLine($"MyAttribute is applied to type {t.Name}");
        Console.ReadKey();
    }
}

output

MyAttribute is applied to type MyClass

6.2 使用GetCustomAttributes方法

GetCustomAttributes方法返回应用到结构的特性的数组。

  • 实际返回的对象时object的数组,因此要强制转换为相应的特性类型
  • 布尔参数指定是否搜索继承树来检查特性
    object[] AttArr = t.GetCustomAttributes(false);
  • 调用GetCustomAttributes方法后,每一个与目标相关联的特性的实例就会被创建
[AttributeUsage(AttributeTargets.Class)]
public sealed class MyAttributeAttribute:System.Attribute
{
    public string Description;
    public string Ver;
    public string Reviewer;
    public MyAttributeAttribute(string desc,string ver)
    {
        Description = desc;
        Ver = ver;
    }
}
[MyAttribute("hello", "0.0.1")]
class MyClass { }
class Program
{
    static void Main()
    {
        Type t = typeof(MyClass);
        object[] AttArr = t.GetCustomAttributes(false);
        foreach (Attribute a in AttArr)
        {
            MyAttributeAttribute attr = a as MyAttributeAttribute;
            if (null != attr)
            {
                Console.WriteLine($"Description: {attr.Description}");
                Console.WriteLine($"Version: {attr.Ver}");
                Console.WriteLine($"Reviewer: {attr.Reviewer}");
            }
        }
        Console.ReadKey();
    }
}

output

Description: hello
Version: 0.0.1
Reviewer:
posted @ 2019-04-03 15:29  Kyle0418  阅读(402)  评论(0编辑  收藏  举报