C# 特性(Attribute)



什么是特性:

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

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




规定特性:

特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集,它可以放置在几乎所有的声明中(但特定的属性可能限制在其上有效的声明类型)。其语法为:

  ● 在结构前放置特性片段来运用特性

  ● 特性片段被方括号包围,其中是特性名和特性的参数列表

  例:

[Serializable]    //不含参数的特性
    public class MyClass
    {...}

   [MyAttribute("firt","second","finally")]    //带有参数的特性
  public class MyClass {...}

单个结构可以运用多个特性,使用时可以把独立的特性片段互相叠在一起或使用分成单个特性片段,特性之间用逗号分隔

[Serializable]  
[MyAttribute("firt","second","finally")]    //独立的特性片段
...


[MyAttribute("firt","second","finally"), Serializable]    //逗号分隔
...

 某些属性对于给定实体可以指定多次。例如,Conditional 就是一个可多次使用的属性:

[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
    // ...
}

特性的目标是应用该特性的实体。例如,特性可以应用于类、特定方法或整个程序集。默认情况下,特性应用于它后面的元素。但是,您也可以显式标识要将特性应用于方法还是它的参数或返回值。


预定义特性:

.Net 框架提供了三种预定义特性:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

其中:

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例如:

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

Conditional

这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。

规定该特性的语法如下:

[Conditional(
   conditionalSymbol
)]

例如:

[Conditional("DEBUG")]

 下面的实例演示了该特性:

#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
    [Conditional("DEBUG")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}
class Test
{
    static void function1()
    {
        Myclass.Message("In Function 1.");
        function2();
    }
    static void function2()
    {
        Myclass.Message("In Function 2.");
    }
    public static void Main()
    {
        Myclass.Message("In Main function.");
        function1();
        Console.ReadKey();
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

In Main function
In Function 1
In Function 2

Obsolete

这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

规定该特性的语法如下:

[Obsolete(
   message
)]
[Obsolete(
   message,
   iserror
)]

其中:

  • 参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
  • 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

下面的实例演示了该特性:

using System;
public class MyClass
{
   [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
   static void OldMethod()
   {
      Console.WriteLine("It is the old method");
   }
   static void NewMethod()
   {
      Console.WriteLine("It is the new method");
   }
   public static void Main()
   {
      OldMethod();
   }
}

当您尝试编译该程序时,编译器会给出一个错误消息说明:

Don't use OldMethod, use NewMethod instead



创建自定义特性:

.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

创建并使用自定义特性包含四个步骤:

  • 声明自定义特性
  • 构建自定义特性
  • 在目标程序元素上应用自定义特性
  • 通过反射访问特性

最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一章详细讨论这点。

声明自定义特性

一个新的自定义特性应派生自 System.Attribute 类。例如:

// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

public class DeBugInfo : System.Attribute

在上面的代码中,我们已经声明了一个名为 DeBugInfo 的自定义特性。

构建自定义特性

让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:

  • bug 的代码编号
  • 辨认该 bug 的开发人员名字
  • 最后一次审查该代码的日期
  • 一个存储了开发人员标记的字符串消息

我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。

每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:

// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

public class DeBugInfo : System.Attribute
{
  private int bugNo;
  private string developer;
  private string lastReview;
  public string message;

  public DeBugInfo(int bg, string dev, string d)
  {
      this.bugNo = bg;
      this.developer = dev;
      this.lastReview = d;
  }

  public int BugNo
  {
      get
      {
          return bugNo;
      }
  }
  public string Developer
  {
      get
      {
          return developer;
      }
  }
  public string LastReview
  {
      get
      {
          return lastReview;
      }
  }
  public string Message
  {
      get
      {
          return message;
      }
      set
      {
          message = value;
      }
  }
}

应用自定义特性

通过把特性放置在紧接着它的目标之前,来应用该特性:

[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
  // 成员变量
  protected double length;
  protected double width;
  public Rectangle(double l, double w)
  {
      length = l;
      width = w;
  }
  [DeBugInfo(55, "Zara Ali", "19/10/2012",
  Message = "Return type mismatch")]
  public double GetArea()
  {
      return length * width;
  }
  [DeBugInfo(56, "Zara Ali", "19/10/2012")]
  public void Display()
  {
      Console.WriteLine("Length: {0}", length);
      Console.WriteLine("Width: {0}", width);
      Console.WriteLine("Area: {0}", GetArea());
  }
}

访问特性:

对于自定义的特性,我们可以用Type中的IsDefined和GetCustomAttributes方法来获取

1, 使用IsDefined方法

public abstract bool IsDefined(Type attributeType, bool inherit),它是用来检测某个特性是否应用到了某个类上

  参数说明:    attributeType : 要搜索的自定义特性的类型。 搜索范围包括派生的类型。

        inherit:true 搜索此成员继承链,以查找这些属性;否则为 false。 属性和事件,则忽略此参数

        返回结果: true 如果一个或多个实例 attributeType 或其派生任何的类型为应用于此成员; 否则为 false。

   下面代码片段是用来检查MyAttribute特性是否被运用到MyClass类

MyClass mc = new MyClass();
Type t = mc.GetType();
bool def = t.IsDefined(typeof(MyAttributeAttribute),false);
if (def)
    Console.WriteLine("MyAttribute is defined!");

2, 使用GetCustomAttributes方法 

public abstract object[] GetCustomAttributes(bool inherit),调用它后,会创建每一个与目标相关联的特性的实例

  参数说明: inherit: true 搜索此成员继承链,以查找这些属性;否则为 false

        返回结果:返回所有应用于此成员的自定义特性的数组,因此我们必须将它强制转换为相应的特性类型

//自定义特性

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
    public class MyAttributeAttribute : System.Attribute
    {
        public string Description;
        public string ver;
        public string Reviwer;

        public MyAttributeAttribute(string desc,string ver,string Rev)
        {
            Description = desc;
            this.ver = ver;
            Reviwer = Rev;
        }
    }

//定义类
[MyAttribute("firt","second","finally")]
    class MyClass
    {

    }

 static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            Type t = mc.GetType();
            Object[] obj = t.GetCustomAttributes(false);

            foreach(Attribute a in obj)
            {
                MyAttributeAttribute attr = a as MyAttributeAttribute;
                if(attr != null)
                {
                    Console.WriteLine("Description : {0}", attr.Description);
                    Console.WriteLine("ver : {0}", attr.ver);
                    Console.WriteLine("review: {0}", attr.Reviwer);
                }
            }
        }    

结果:

 Description : firt

ver:second

review:finally 


预定义的特性:

1, Obsolete特性

Obsolete特性将public ObsoleteAttribute()程序结构标注为过期的,并且在代码编译时显式有用的警告信息,它有三种重载

  public ObsoleteAttribute()

  public ObsoleteAttribute(string message)  参数说明: message:描述了可选的变通方法文本字符串。

  public ObsoleteAttribute(string message, bool error)  参数说明:message:描述了可选的变通方法文本字符串。  error:true 如果使用过时的元素将生成编译器错误; false 如果使用它将生成编译器警告。

  举个例子:

using System;
using System.Runtime.CompilerServices;

namespace 特性
{
    class Program
    {
        [Obsolete("Use method SuperPrintOut")]
        static void Print(string str,[CallerFilePath] string filePath = "")
        {
            Console.WriteLine(str);
            Console.WriteLine("filePath {0}", filePath);
        }


        static void Main(string[] args)
        {
            string path = "no path";
            Print("nothing",path);
            Console.ReadKey();
        }
    }
}

2, Conditional 特性

 public ConditionalAttribute(string conditionString),指示编译器,如果定义了conditionString编译符号,就和普通方法没有区别,否则忽略代码中方法这个方法的所有调用

#define fun    //定义编译符号
using System;
using System.Runtime.CompilerServices;

namespace 特性
{
    class Program
    {
        [Conditional("fun")]
        static void Fun(string str)
        {
            Console.WriteLine(str);
        }

        static void Main(string[] args)
        {
            Fun("hello");
            Console.ReadKey();
        }
    }

}

由于定义了fun,所以Fun函数会被调用,如果没有定义,这忽略Fun函数的调用

3, 调用者信息特性 

调用者信息特性可以访问文件路径、代码行数、调用成员的名称等源代码信息,这三个特性的名称分别为CallerFilePath、CallerLineNumber和CallerMemberName,这些方法只能用于方法中的可选参数

using System;
using System.Runtime.CompilerServices;

namespace 特性
{
    class Program
    {
        static void Print(string str,
            [CallerFilePath] string filePath = "",
            [CallerLineNumber] int num = 0,
            [CallerMemberName] string name = "")
        {
            Console.WriteLine(str);
            Console.WriteLine("filePath {0}", filePath);
            Console.WriteLine("Line {0}", num);
            Console.WriteLine("Call from {0}", name);
        }

        static void Main(string[] args)
        {
            Print("nothing");
            Console.ReadKey();
        }
    }
}

结果:

nothing

filePath E:\C#实验\特性\特性\Program.cs

Line 22

Call from Main


  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Attribute特性是一种用于给程序元素添加额外信息的机制。在C#中,Attribute特性通常以Attribute后缀命名,并且可以附加到类、方法、属性等各种程序元素上。当Attribute特性被应用到程序元素上时,它们可以被用来提供元数据,以帮助编译器、运行时或其他代码分析工具进行处理。 引用中提到了一个约定:自定义的特性名称应该以Attribute后缀结尾。这是因为在将Attribute特性应用到程序元素上时,编译器首先会查找使用的Attribute的定义,如果找不到,则会尝试查找以"Attribute"结尾的同名特性的定义。 在引用中,提到了Attribute特性在ASP.NET开发中的应用。这表明Attribute特性在ASP.NET开发中是非常常见的,并且被广泛使用。 举个例子,引用给出了一个自定义的Attribute特性的示例代码。在这个示例中,定义了一个名为CustomAttribute特性类,它具有一个属性Desc,并提供了两个构造函数。这个示例展示了如何创建自定义的Attribute特性,并将其应用到一个名为Student的类上。 总之,Attribute特性C#中用于给程序元素添加元数据,以提供额外的信息和行为。它们是一种强大的工具,可以用于实现各种功能和扩展。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C# 特性Attribute)](https://blog.csdn.net/qq_42335551/article/details/130268405)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Attribute/特性心得随笔](https://download.csdn.net/download/weixin_38607552/13055291)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值