C# 特性(Attributes)

用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。特性与程序实体相关联后,即可在运行时用反射技术查询特性。

例如,在一个方法前标注[Obsolete]特性,则调用该方法时VS则会提示该方法已过期的警告,如下图:
C# 特性(Attributes)_第1张图片

又如,在.Net Remoting的远程对象中,如果要调用或传递某个对象,例如类,或者结构,则该类或结构则必须标注[Serializable]特性。还有,我们在构建XML Web服务时用得很多的一个特性就是[WebMegthod],它可让通过HTTP请求的公开方法的返回值编码成XML进行传递。

特性实际上就是一个类,[Obsolete]特性的实际类名是ObsoleteAttribute,但我们在标注的时候可以不带Attribute后缀,系统在名称转换时会自动给我们加上。

上面说的都是些.NET系统定义的一些特性,当然还有很多。了解如何自定义特性,有利有我们更好的在ASP.NET MVC编程使用特性,比如给Model类的属性标注特性来验证表单输入的合法性(以后进行介绍)。

下面我们来模拟一个ASP.NET MVC经常要用到的StringLenth特性,它用于判断用户输入是否超出长度限制。我们现在来模拟它。先定义一个MyStringLenth特性:

复制代码
// 用户自定义的带有可选命名参数的 MyStringLenthAttribute 特性类。
// 该特性通过AttributeUsage限制它只能用在属性和字段上。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class MyStringLenthAttribute : Attribute {
    public MyStringLenthAttribute(string displayName, int maxLength) {
        this.MaxLength = maxLength;
        this.DisplayName = displayName;
    }
    //显示的名称,对外是只读的,所以不能通过可选参数来赋值,必须在构造函数中对其初始化。
    public string DisplayName { get; private set; }

    //长度最大值,对外是只读的,所以不能通过可选参数来赋值,必须在构造函数中对其初始化。
    public int MaxLength { get; private set; }

    //错误信息,标注时可作为可选命名参数来使用。
    public string ErrorMessage { get; set; }

    //长度最小值,标注时可作为可选命名参数来使用。
    public int MinLength { get; set; }
}
复制代码

上面若不加AttributeUsage限制,特性可以声明在类型(如结构、类、枚举、委托)和成员(如方法,字段,事件,属性,索引)的前面。

然后我们把这个特性应用在下面的Order类之上:

// 应用自定义MyStringLenth特性于Order类的OrderID属性之上。MinLength和ErrorMessage是命名参数。
public class Order {
    [MyStringLenth("订单号", 6,MinLength = 3, ErrorMessage = "{0}的长度必须在{1}和{2}之间,请重新输入!")]
    public string OrderID { get; set; }
}

最后我们看看如何使用MyStringLenth特性验证用户输入字符串的长度:

复制代码
//检查成员字符串长度是否越限。
private static bool IsMemberValid(int inputLength, MemberInfo member) {
    foreach (object attribute in member.GetCustomAttributes(true)) {
        if (attribute is MyStringLenthAttribute) {
            MyStringLenthAttribute attr=(MyStringLenthAttribute)attribute;
            string displayName = attr.DisplayName;
            int maxLength = attr.MaxLength;
            int minLength = attr.MinLength;
            string msg = attr.ErrorMessage;

            if (inputLength < minLength || inputLength > maxLength) {
                Console.WriteLine(msg, displayName, minLength, maxLength);
                return false;
            }
            else {
                return true;
            }
        }
    }
    return false;
}

//验证输入是否合法
private static bool IsValid(Order order) {
    if (order == null) return false;

    foreach (PropertyInfo p in typeof(Order).GetProperties()) {
        if (IsMemberValid(order.OrderID.Length, p))
            return true;
    }

    return false;
}

public static void Main() {
    string input=string.Empty;
    Order order;
    do {
        Console.WriteLine("请输入订单号:");
        input = Console.ReadLine();
        order = new Order { OrderID = input };
    }
    while (!IsValid(order));
    Console.WriteLine("订单号输入正确,按任意键退出!");
    Console.ReadKey();
}
复制代码

输出效果如下:
C# 特性(Attributes)_第2张图片

System.AttributeUsage声明一个Attribute的使用范围与使用原则。

  C# 特性(Attributes)_第3张图片

  AllowMultiple 和 Inherited 参数是可选的,所以此代码具有相同的效果:

  

  AttributeTarget的值可以参考1。部分可取值如下:

  C# 特性(Attributes)_第4张图片

  如果 AllowMultiple 参数设置为 true,则返回特性可对单个实体应用多次。

  如果 Inherited 设置为 false,则该特性不由从特性化的类派生的类继承。

  Attribute.GetCustomAttribute可以获取一个类的Attribute。

你可能感兴趣的:(C#)