C#特性(Attribute)

为什么需要特性呢?某些情况下需要给类或者方法添加一些标签信息,比如我们在调试的时候为方法注明调试事件,调试人等等信息。根据定义方式的不同,特性分为系统提供的特性以及自定义的特性

1、预定义特性

.Net Framework 中提供了三个预定义的属性:

  • AttributeUsage;
  • Conditional;
  • Obsolete。

1) AttributeUsage

预定义特性 AttributeUsage 用来描述如何使用自定义特性类,其中定义了可以应用特性的项目类型。AttributeUsage 的语法格式如下:

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

参数说明如下:

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

示例代码如下:

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

2) Conditional

预定义特性 Conditional 用来标记一个方法,它的执行依赖于指定的预处理标识符。根据该特性值的不同,在编译时会起到不同的效果,例如当值为 Debug 或 Trace 时,会在调试代码时显示变量的值。

预定义特性 Conditional 的语法格式如下:

[Conditional(
    conditionalSymbol
)]

【示例】下面通过示例演示预定义特性 Conditional 的使用。

#define BUG
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;


namespace helloworld
{
    public class Myclass
    {
        [Conditional("BUG")]
        public static void M1(string msg)
        {
            Console.WriteLine(msg);
        }
        [Conditional("deBUG")]
        public static void M2(string msg)
        {
            Console.WriteLine(msg);
        }
        [Conditional("BUG")]
        public static void M3(string msg)
        {
            Console.WriteLine(msg);
        }
        [Conditional("kk")]
        public static void M4(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Myclass.M1("456");
            Myclass.M2("845");
            Myclass.M3("dadasd");
            Myclass.M4("456xcvc");
            Console.WriteLine("Main thread ID is:" );
            Console.ReadKey();
        }
    }
}

运行结果如下:

456

dadasd

Main thread ID is:

将头部的#define BUG就会运行M1和M3,定义为#define kk就将会只运行M4方法。

但是注意如果没有写 [] 的都会执行,所以一般加 []attrible 时候,常常看见如果有加一般每个方法上面都会加。

3) Obsolete

预定义特性 Obsolete 用来标记不应被使用的程序,您可以使用它来通知编译器放弃某个目标元素。例如当您需要使用一个新方法来替代类中的某个旧方法时,就可以使用该特性将旧方法标记为 obsolete(过时的)并来输出一条消息,来提示我们应该使用新方法代替旧方法。

预定义特性 Obsolete 的语法格式如下:

[Obsolete (
   message
)]

[Obsolete (
   message,
   iserror
)]

语法说明如下:

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

【示例】下面通过示例来演示预定义特性 Obsolete 的使用:

using System;

namespace c.biancheng.net
{
    class Demo
    {
        [Obsolete("OldMethod 已弃用,请改用 NewMethod", true)]
        static void OldMethod()
        {
          Console.WriteLine("已弃用的函数");
        }
        static void NewMethod()
        {
          Console.WriteLine("新定义的函数");
        }
        static void Main(string[] args) 
        {
            OldMethod();
        }
    }
}

运行结果如下:

demo.cs(18,10): error CS0619: “c.biancheng.net.Demo.OldMethod()”已过时:“OldMethod 已弃用,请改用 NewMethod”

2、自定义特性

.Net Framework 允许您创建自定义特性,自定义特性不仅可以用于存储声明性的信息,还可以在运行时被检索。创建并使用自定义特性可以分为四个步骤:

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


最后一步涉及编写一个简单的程序来读取元数据以便查找各种符号。元数据是有关数据或用于描述其他数据信息的数据。该程序应在运行时使用反射来访问属性。

1) 声明自定义属性

自定义特性应该继承 System.Attribute 类,如下所示:

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

public class DeBugInfo : System.Attribute

在上面的示例代码中,我们声明了一个名为 DeBugInfo 的自定义属性。

2) 构建自定义特性

让我们构建一个名为 DeBugInfo 的自定义特性,该特性可以存储下面列举的调试信息:

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


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

每个特性都至少有一个构造函数,而且定位( positional)参数需要通过构造函数传递。如下例所示:

// 一个自定义特性 DeBugInfo 被赋给类及其成员
[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;
      }
  }
}

3) 应用自定义特性

通过把特性放置在紧挨着它的目标上面来应用该特性,示例代码如下:

using System;

namespace c.biancheng.net
{
    class Demo
    {
        static void Main(string[] args) 
        {
            Rectangle rec = new Rectangle(12, 15);
            rec.Display();
        }
    }

    [DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "返回值类型不匹配")]
    [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "未使用变量")]
    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 = "返回值类型不匹配")]
        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());
        }
    }
    // 一个自定义特性 DeBugInfo 被赋给类及其成员
    [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;
            }
        }
    }
}

运行结果如下:

Length: 12
Width: 15
Area: 180

而所谓的反射则是把类或方法的标签信息提取出来。

比如:

Rectangele rect=new Rectangle(); 
Type type=typeof(Rectangle); 
foreach(DeBugInfo attributes in type.GetCustomAttributes(false)) { 
    //打印dbi属性 
    Console.WriteLine("bug 的代码编号:{0}", attributes.BugNo); 
    Console.WriteLine("bug 的开发人员名字:{0}",attributes.Developer);
    Console.WriteLine("上次审查代码的日期:{0}\n",attributes.LastReview);
} ​

运行结果如下:

bug 的代码编号:45
bug 的开发人员名字:Zara Ali
上次审查代码的日期:12/8/2012
bug 的代码编号:49
bug 的开发人员名字:Nuha Ali
上次审查代码的日期:10/10/2012

结果显示的是使用在DeBugInfo类上的标签信息。

来源:C#特性(Attribute) (biancheng.net)C# 特性(Attribute) | 菜鸟教程 (runoob.com)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值