C# 高级篇24-27

24. 特性(Attribute)

概述:特性是指可以对类、以及程序集中的成员进行进一步的描述,比如写一个关于人的类Person,该类可以对人的属性以及某些行为(方法)进行描述。如果要对人类进行进一步描述,比如人这个类是属于动物的灵长类动物。有人会说可以为这个Person类去写一个灵长动物类的父类,再用人类去继承这个类去解决。但要求仅仅是描述性的,就是对这个人类进行进一步的描述,而在实际操作中不需要去操作。这种情况就可以用特性的概念去解决,特性简单的理解就是程序集的特定程序元素具有另外的性质。

  • 特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
  • 特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
    规定特性(Attribute)
    规定特性(Attribute)的语法如下:

[attribute(positional_parameters, name_parameter = value, ...)]
element

  • 特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
24.1 预定义特性(Attribute)
24.1.1 AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。
    [AttributeUsage(AttributeTargets.Class |
    AttributeTargets.Constructor |
    AttributeTargets.Field |
    AttributeTargets.Method |
    AttributeTargets.Property,
    AllowMultiple = true)]
24.1.2 Conditional

这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。
它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。
[Conditional(
conditionalSymbol
)]
[Conditional("DEBUG")]

24.1.3 Obsolete

这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

24.2 创建自定义特性(Attribute)

.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。创建并使用自定义特性包含四个步骤:

  1. 声明自定义特性
  2. 构建自定义特性
  3. 在目标程序元素上应用自定义特性
  4. 通过反射访问特性

25. 反射(Reflection)

  • 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
  • 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
  • 您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优点:
1、反射提高了程序的灵活性和扩展性。
2、降低耦合性,提高自适应能力。
3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:
1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

反射(Reflection)的用途

  • 允许在运行时查看特性(attribute)信息。
  • 允许审查集合中的各种类型,以及实例化这些类型。
  • 允许延迟绑定的方法和属性(property)。
  • 允许在运行时创建新类型,然后使用这些类型执行一些任务。
using System;
// 对人类进行动物类描述。即在人类的定义前面加:
[Animal(IsPrimate = true)]//为人类加特性,指定人类是灵长类。
class Person
{
    //人的姓名储存字段和属性
    private string name;
    public string Name
    {
        set { name = value; }
        get { return name; }
    }
    //人的年龄储存字段和属性
    private int age;
    public int Age
    {
        set { age = value; }
        get { return age; }
    }
    //人的性别储存字段和属性
    private char sex;
    public char Sex
    {
        set { sex = value; }
        get { return sex; }
    }
    //人的打招呼方法
    public void SayHello()
    {
        Console.WriteLine("大家好,我叫{0},我今年{1}岁了,我的性别是{2}", this.Name, this.Age, this.Sex);
    }
}
// 动物的特性类AnimalAttribute类继承于Attribute(特性)
 class AnimalAttribute : Attribute
{
    //字段和属性描述是否是灵长类
    private bool isPrimate;
    public bool IsPrimate
    {
        set { isPrimate = value; }
        get { return isPrimate; }
    }
}
class Test
{
    static void Main(string[] args)
    {
        // 通过代码来获得人类的特性:
        //声明特性对象,并通过Attribute类的静态方法GetCustomAttribute()获得人类的在动物类的特性,并赋值给特性对象
        Attribute att1 = Attribute.GetCustomAttribute(typeof(Person), typeof(AnimalAttribute));
        //将特性对象转化为动物特性对象
        AnimalAttribute animalAtt = att1 as AnimalAttribute;
        //检查转化是否成功如果成功则打印这个特性对象的是否是灵长类的属性。
        if (animalAtt != null)
        {
            Console.WriteLine("人类是否是灵长类:{0}", animalAtt.IsPrimate);
        }
        Console.ReadKey();
    }
}

26. 属性(Property)

域Field 与 属性Property{get,set}

  • 属性 是类(class)、结构(structure)和接口(interface)的命名(named)成员。
  • 类或结构中的成员变量或方法称为 域(Field)。
  • 属性是域(Field)的扩展,且可使用相同的语法来访问。
  • 属性使用 访问器(accessors) 让私有域的值可被读写或操作。
  • 属性不会确定存储位置。相反,它们具有可读写或计算它们值的 访问器(accessors)。
  • 访问器(Accessors):属性的访问器(accessor)包含有助于获取(读取或计算)或设置(写入)属性的可执行语句。访问器(accessor)声明可包含一个 get 访问器、一个 set 访问器,或者同时包含二者。
  • 抽象属性(Abstract Properties):抽象类可拥有抽象属性,这些属性应在派生类中被实现。

例如,有一个名为 Student 的类,带有 age、name 和 code 的私有域。我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。

27. 索引器(Indexer)

  • 索引器(Indexer) 允许一个对象可以像数组一样使用下标的方式来访问.
  • 当您为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array) 一样。您可以使用数组访问运算符 [ ] 来访问该类的的成员。
  • 索引器的行为的声明在某种程度上类似于属性(property)。就像属性(property),您可使用 get 和 set 访问器来定义索引器。但是,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。换句话说,它把实例数据分为更小的部分,并索引每个部分,获取或设置每个部分。
  • 定义一个属性(property)包括提供属性名称。索引器定义的时候不带有名称,但带有 this 关键字,它指向对象实例。
  • 索引器(Indexer)可被重载。索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型。没有必要让索引器必须是整型的。C# 允许索引器可以是其他类型.
element-type this[int index]
{
   // get 访问器
   get
   {
      // 返回 index 指定的值
   }

   // set 访问器
   set
   {
      // 设置 index 指定的值
   }
}
using System;
namespace IndexerApplication
{
   class IndexedNames
   {
      private string[] namelist = new string[size];
      static public int size = 10;
      public IndexedNames()
      {
         for (int i = 0; i < size; i++)
         {
          namelist[i] = "N. A.";
         }
      }
      public string this[int index]
      {
         get
         {
            string tmp;

            if( index >= 0 && index <= size-1 )
            {
               tmp = namelist[index];
            }
            else
            {
               tmp = "";
            }

            return ( tmp );
         }
         set
         {
            if( index >= 0 && index <= size-1 )
            {
               namelist[index] = value;
            }
         }
      }
      public int this[string name]
      {
         get
         {
            int index = 0;
            while(index < size)
            {
               if (namelist[index] == name)
               {
                return index;
               }
               index++;
            }
            return index;
         }

      }

      static void Main(string[] args)
      {
         IndexedNames names = new IndexedNames();
         names[0] = "Zara";
         names[1] = "Riz";
         names[2] = "Nuha";
         names[3] = "Asif";
         names[4] = "Davinder";
         names[5] = "Sunil";
         names[6] = "Rubic";
         // 使用带有 int 参数的第一个索引器
         for (int i = 0; i < IndexedNames.size; i++)
         {
            Console.WriteLine(names[i]);
         }
         // 使用带有 string 参数的第二个索引器
         Console.WriteLine(names["Nuha"]);
         Console.ReadKey();
      }
   }
}

你可能感兴趣的:(C# 高级篇24-27)