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

一、引言        

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

 
  1. [Obsolete("该类已经过时,不能再使用",true)]

  2. public class Human

  3. {

  4. public string Name { get; set; }

  5. public Human Child { get; set; }

  6. }

        当我们在代码中使用该类时,发现编译器会报错,并提示我们自定义的提示信息。

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

 
  1. class Program

  2. {

  3. static void Main(string[] args)

  4. {

  5. Human human;

  6. }

  7. }

        这是编译器报的错误:

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

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

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

object[] attributes = typeof(Human).GetCustomAttributes(true);

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

三、自定义特性

在前面的例子中,我们提到过,特性的本质其实就是一个类,这个类必须继承自Attribute基类,而且类名一般要求格式为“名称+Attribute”的形式。我们现在为Human类定义一个Help的属性,来告诉使用者Human类的信息:

 
  1. public class HelpAttribute : Attribute

  2. {

  3. public Help(string info)

  4. {

  5. this.Info = info;

  6. }

  7. public string Info { get; private set; }

  8. }

我们在使用时可以加Attribute或者不加Attribute,如下面的格式:

[Help("这是一个Human类")]

或者

 [HelpAttribute("这是一个Human类")]

因为编译器在处理特性时会先查找特性名称,如果找不到就自动加上Attribute再次进行查找。

我们在主函数中查找类的特性并输出:

 
  1. static void Main(string[] args)

  2. {

  3. object[] attributes = typeof(Human).GetCustomAttributes(true);

  4. HelpAttribute attribute=attributes[0] as HelpAttribute;

  5. Console.WriteLine(attribute.Info);

  6. }

得到的结果如下:



 

四、使用转换类TypeConvertAttribute

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

 
  1. public class StringToHumanConverter : TypeConverter

  2. {

  3. public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)

  4. {

  5. if (value is String)

  6. {

  7. Human h = new Human();

  8. h.Name = value as String;

  9. return h;

  10. }

  11. return base.ConvertFrom(context, culture, value);

  12. }

  13. }

        接下来,将特性赋予Human类 :

 
  1. [TypeConverterAttribute(typeof(StringToHumanConverter))]

  2. public class Human

  3. {

  4. public string Name { get; set; }

  5. public Human Child { get; set; }

  6. }

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

Human human = str as Human;

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

 
  1. String str = "hyman";

  2. StringToHumanConverter convert = new StringToHumanConverter();

  3. Human human = (Human)convert.ConvertFrom(str);

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

 
  1. <Window.Resources>

  2. <local:Human x:Key="human" Child="tom"/>

  3. </Window.Resources>

        我们在后台代码中可以通过FindResource找到该资源,发现已经可以正常打印出其Child的Name,这是因为WPF的框架自动调用TypeConverter进行了转换:

 
  1. private void button1_Click(object sender, RoutedEventArgs e)

  2. {

  3. Human human = (Human)this.FindResource("human");

  4. MessageBox.Show(human.Child.Name);

  5. }

猜你喜欢

转载自blog.csdn.net/weixin_42339460/article/details/81183473