C# 中的特性(Attributes)-3

  • C# 中的特性(Attributes)-1
  • C# 中的特性(Attributes)-2
  • C# 中的特性(Attributes)-3
  • C# 中的特性(Attributes)-4

可选参数 vs. 命名参数

可选参数是attribute类构造函数的参数。它们是强制的,必须在每次在attribute绑定至某语言元素时提供一个值。而另一方面,命名参数倒是真正的可选参数,不是在attribute构造函数的参数。

为了更加详细的解释,让我们在Help类中添加另外的属性。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Description_in)
    {
        this.description = Description_in;
        this.verion = "No Version is defined for this class";
    }
   
    protected String description;
   
    public String Description
    {
        get
        {
            return this.description;
        }
    }
   
    protected String version;
   
    public String Version
    {
        get
        {
            return this.version;
        }
        //if we ever want our attribute user to set this property,
        //we must specify set method for it
        set
        {
            this.verion = value;
        }
    }
}
 
[Help("This is Class1")]
public class Class1
{
}
 
[Help("This is Class2", Version = "1.0")]
public class Class2
{
}
 
[Help("This is Class3", Version = "2.0", Description = "This is do-nothing class")]
public class Class3
{
}

当我们在Class1中查询Help attribute已经它的属性,我们将得到:

Help.Description : This is Class1
Help.Version :No Version is defined for this class

因为我们没有为Version这个属性定义任何任何值,所以在构造函数中设定的值被我们查询出来了。如果没有定义任何值,那么就会赋一个该类型的默认值(例如:如果是int型,默认值就是0)。

现在,查询Class2的结果是:

Help.Description : This is Class2
Help.Version :  1.0

我们不能为了可选参数而使用多个构造函数,应该用已命名参数来代替。我们之所以称它们为已命名的,是因为当我们在构造函数为它们提供值时,我们必须命名它们。例如,在第二个类中,我们如是定义Help。

[Help("This is Class2", Version = "1.0")]

在 AttributeUsage 例子中, 参数”ValidOn”是可选参数,而“Inherited“和“AllowMultiple“ 是命名参数。

注意:为了在attribute的构造函数中设定命名参数的值,我们必须为相应的属性提供一个set方法否则会引起编译期错误:

'Version' : Named attribute argument can't be a read only property

现在,我们在Class3中查找Help attribute 及其属性会发生什么呢?结果是跟上面提到的相同的编译期错误。

'Desciption' : Named attribute argument can't be a read only property

现在我们修改下Help类,为属性”Description”加一个set方法。现在的输出就是:

Help.Description : This is do-nothing class
Help.Version : 2.0

在屏幕后面究竟发生了什么呢?首先带有可选参数的构造函数被调用,然后,每个命名参数的set方法被调用,在构造函数中赋给命名参数的值被set方法所覆写。

参数类型

一个attribute类的参数类型被限定在如下类型中:

  • bool
  • byte
  • char
  • double
  • float
  • int
  • long
  • short
  • string
  • System.Type
  • object
  • An enum type, provided that it and any types in which it is nested are publicly accessible. A one-dimensional array involving any of the types listed above

Attributes 标记

假设,我们想把Help attribute 绑定至元素 assembly。第一个问题是我们要把Help attribute 放在哪儿才能让编译器确定该attribute是绑定至整个assembly呢?考虑另一种情况,我们想把attribute绑定至一个方法的返回类型上,怎样才能让编译器确定我们是把attribute绑定至方法的返回类型上,而不是整个方法呢?

为了解决诸如此类的含糊问题,我们使用attribute标识符,有了它的帮助,我们就可以确切地申明我们把attribute 绑定至哪一个语言元素。

例如:

[assembly: Help("this a do-nothing assembly")]

这个在Help attribute 前的assembly标识符确切地告诉编译器,该attribute被绑定至整个assembly。可能的标识符有: 

  • assembly
  • module
  • type
  • method
  • property
  • event
  • field
  • param
  • return

在运行时查询Attributes

现在我们明白怎么创建attribtes和把它们绑定至语言元素。是时候来学习类的使用者该如何在运行时查询这信息。

为了查询一语言元素上绑定的attributes,我们必须使用反射。反射有能力在运行时发现类型信息。

我们可以使用.NET Framework Reflection APIs 通过对整个assembly元数据的迭代,列举出assembly中所有已定义的类,类型,还有方法。

记住那旧的Help attribute 和AnyClass 类。

using System;
using System.Reflection;
using System.Diagnostics;
 
//attaching Help attribute to entire assembly
[assembly : Help("This Assembly demonstrates custom attributes creation and their run-time query.")]
//our custom attribute class
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Description_in)
    {
        //
        // TODO: Add constructor logic here
        this.description = Description_in;
        //
    }
   
    protected String description;
   
    public String Description
    {
        get
        {
            return this.deescription;
        }
    }   
}
 
//attaching Help attribute to our AnyClass
[HelpString("This is a do-nothing Class.")]
public class AnyClass
{
    //attaching Help attribute to our AnyMethod
    [Help("This is a do-nothing Method.")]
    public void AnyMethod()
    {
    }
   
    //attaching Help attribute to our AnyInt Field
    [Help("This is any Integer.")]
    public int AnyInt;
}
 
class QueryApp
{
    public static void Main()
    {
    }
}

 

  • C# 中的特性(Attributes)-1
  • C# 中的特性(Attributes)-2
  • C# 中的特性(Attributes)-3
  • C# 中的特性(Attributes)-4

你可能感兴趣的