【C#语法】类和方法的特性Attribute

来源:互联网 发布:c语言求最短路径 编辑:程序博客网 时间:2024/06/03 06:50

一、引言        

        今天,我们来聊一下C#中的特性Attribute。何为特性Attribute?我们先看一个特性的使用例子,我们定义了一个Human类,但是由于某种原因该类不再被使用,但是我又不想将该类的代码注释或者删除掉,于是我们就可以通过给它赋予Obsolete特性来禁止别人使用:

    [Obsolete("该类已经过时,不能再使用",true)]    public class Human    {        public string Name { get; set; }        public Human Child { get; set; }    }
        当我们在代码中使用该类时,发现编译器会报错,并提示我们自定义的提示信息。

        这是在main函数中使用Human类:

class Program    {        static void Main(string[] args)        {            Human human;        }    }
        这是编译器报的错误:

        我们在Human类前面添加的obsolete就是该类的一个特性,与之相对,我们接触最多的应该是类的属性Property,Human类的Name和Child就是其两个属性。特性本质就是一个类,Obsolete中传递的两个参数就是该类的构造函数所需的参数,第一个是在编译出错时编译器要提示的字符串,第二个是指是否 报错,如果为true编译时就会报错。特性除了可以放到类前面之外,也可以放到方法和属性的前面。

二、特性属于类而非属于对象

        在很多的资料中,都把特性比作类在某一特性环境下的属性。比如对于Human类,把一个人放到学校中它就有年级等特性,把它放到公司中就有部门等特性,这种特性不是它与生既来的(与生既来的是属性Proerty),而是随着环境的不同而发生变化。虽然这种解释很形象,但是很容易让人发生误解——某一个对象具有特性!而实际上,特性是属于类而非一个对象,这点类似于类的静态成员,我们不能通过一个human实例获取Human类的特性,只能通过诸如以下的语句来获取其特性:

object[] attributes = typeof(Human).GetCustomAttributes(true);
        其实,要想理解这个问题。我们需要了解特性的用途:特性不是给程序员用的,而是给编译器看的。它告诉编译器这个类具有何特性,在何种情况下应该作何处理!比如在上面的类型中,告诉编译器该类已经不能被使用了;再比如我们在类前面添加 [Serializable]特性,告诉编译器,该类支持序列化。

三、自定义特性

在前面的例子中,我们提到过,特性的本质其实就是一个类,这个类必须继承自Attribute基类,而且类名一般要求格式为“名称+Attribute”的形式。我们现在为Human类定义一个Help的属性,来告诉使用者Human类的信息:
    public class HelpAttribute : Attribute    {        public Help(string info)        {            this.Info = info;        }        public string Info { get; private set; }    }
我们在使用时可以加Attribute或者不加Attribute,如下面的格式:
[Help("这是一个Human类")]
或者
 [HelpAttribute("这是一个Human类")]
因为编译器在处理特性时会先查找特性名称,如果找不到就自动加上Attribute再次进行查找。
我们在主函数中查找类的特性并输出:
static void Main(string[] args)        {            object[] attributes = typeof(Human).GetCustomAttributes(true);            HelpAttribute attribute=attributes[0] as HelpAttribute;            Console.WriteLine(attribute.Info);        }
得到的结果如下:



四、使用转换类TypeConvertAttribute

        接下来,定义一个用于进行类型转换的类,我们将字符串转换成human类对象:

public class StringToHumanConverter : TypeConverter    {        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)        {            if (value is String)            {                Human h = new Human();                h.Name = value as String;                return h;            }            return base.ConvertFrom(context, culture, value);        }    }
        接下来,将特性赋予Human类 :

    [TypeConverterAttribute(typeof(StringToHumanConverter))]    public class Human    {        public string Name { get; set; }        public Human Child { get; set; }    }

        我们定义的这个类,不能在cs代码中直接用来将字符串和Human类对象的隐式转换,诸如下面的用法是错误的:

Human human = str as Human;
       我们想在cs代码中进行转换,只能定义一个StringToHumanConverter对象,调用其ConverFrom方法进行转换:

String str = "hyman";StringToHumanConverter convert = new StringToHumanConverter();Human human = (Human)convert.ConvertFrom(str);

        但是这种转换可以在XMAL中直接使用:

    <Window.Resources>        <local:Human x:Key="human"  Child="tom"/>    </Window.Resources>
        我们在后台代码中可以通过FindResource找到该资源,发现已经可以正常打印出其Child的Name,这是因为WPF的框架自动调用TypeConverter进行了转换:

private void button1_Click(object sender, RoutedEventArgs e)        {            Human human = (Human)this.FindResource("human");            MessageBox.Show(human.Child.Name);        }