首先要说的是,可能一些刚接触C#的朋友常常容易把属性(Property)跟特性(Attribute)弄混淆,其实这是两种不同的东西。属性就是面向对象思想里所说的封装在类里面的数据字段,其形式为:

1: public class HumanBase

2: {

3:     public string Name { get; set; }

4:     public int Age { get; set; }

5:     public int Gender { get; set; }

6: }

在HumanBase这个类里出现的字段都叫属性(Property),而特性(Attribute)又是怎样的呢?

1: [Serializable]

2: public class HumanBase

3: {

4:     public string Name { get; set; }

5:     public int Age { get; set; }

6:     public int Gender { get; set; }

7: }

简单地讲,我们在HumanBase类声明的上一行加了一个[Serializable],这就是特性(Attribute),它表示HumanBase是可以被序列化的,这对于网络传输是很重要的,不过你不用担心如何去理解它,如何理解就是我们下面要探讨的。

C#的特性可以应用于各种类型和成员。前面的例子将特性用在类上就可以被称之为“类特性”,同理,如果是加在方法声明前面的就叫方法特性。无论它们被用在哪里,无论它们之间有什么区别,特性的最主要目的就是自描述。并且因为特性是可以由自己定制的,而不仅仅局限于.NET提供的那几个现成的,因此给C#程序开发带来了相当大的灵活性和便利。

我们还是借用生活中的例子来介绍C#的特性机制吧。

假设有一天你去坐飞机,你就必须提前去机场登机处换登机牌。登机牌就是一张纸,上面写着哪趟航班、由哪里飞往哪里以及你的名字、座位号等等信息,其实,这就是特性。它不需要你生理上包含这些属性(人类出现那会儿还没飞机呢),就像上面的HumanBase类没有IsSerializable这样的属性,特性只需要在类或方法需要的时候加上去就行了,就像你不总是在天上飞一样。

当我们想知道HumanBase是不是可序列化的,可以通过:

1: static void Main(string[] args)

2: {

3:     Console.WriteLine(typeof(HumanBase).IsSerializable);

4:

5:     Console.ReadLine();

6: }

拿到了登机牌,就意味着你可以合法地登机起飞了。但此时你还不知道你要坐的飞机停在哪里,不用担心,地勤人员会开车送你过去,但是他怎么知道你是哪趟航班的呢?显然还是通过你手中的登机牌。所以,特性最大的特点就是自描述。

既然是起到描述的作用,那目的就是在于限定。就好比地勤不会把你随便拉到一架飞机跟前就扔上去了事,因为标签上的说明信息就是起到限定的作用,限定了目的地、乘客和航班,任何差错都被视为异常。如果前面的HumanBase不加上Serializable特性就不能在网络上传输。

我们在顺带来介绍一下方法特性,先给HumanProperty加上一个Run方法:

1: [Serializable]

2: public class HumanPropertyBase

3: {

4:     public string Name { get; set; }

5:     public int Age { get; set; }

6:     public int Gender { get; set; }

7:

8:     public void Run(int speed)

9:     {

10:         // Running is good for health.

11:     }

12: }

只要是个四肢健全、身体健康的人就可以跑步,那这么说,跑步就是有前提条件的,至少是四肢健全,身体健康。由此可见,残疾人和老年人如果跑步就会出问题。假设一个HumanBase的对象代表的是一位耄耋老人,如果让他当刘翔的陪练,那就直接光荣了。如何避免这样的情况呢,我们可以在Run方法中加一段逻辑代码,先判断Age大小,如果小于2或大于60直接抛异常,但是2-60岁之间也得用Switch来分年龄阶段地判断speed参数是否合适,那么逻辑就相当臃肿。简而言之,如何用特性表示一个方法不能被使用呢?OK, here we go:

1: [Obsolete("I'm so old, don't kill me!", true)]

2: public virtual void Run(int speed)

3: {

4:     // Running is good for health.

5: }

上面大致介绍了一下特性的使用与作用,接下来我们要向大家展示的是如何通过自定义特性来提高程序的灵活性,如果特性机制仅仅能使用.NET提供的那几种特性,不就太不过瘾了么。

首先,特性也是类。不同于其它类的是,特性都必须继承自System.Attribute类,否则编译器如何知道谁是特性谁是普通类呢。当编译器检测到一个类是特性的时候,它会识别出其中的信息并存放在元数据当中,仅此而已,编译器并不关心特性说了些什么,特性也不会对编译器起到任何作用,正如航空公司并不关心每个箱子要去哪里,只有箱子的主人和搬运工才会去关心这些细节。假设我们现在就是航空公司的管理人员,需要设计出前面提到的登机牌,那么很简单,我们先看看最主要的信息有哪些:

1: public class BoardingCheckAttribute : Attribute

2: {

3:     public string ID { get; private set; }

4:     public string Name { get; private set; }

5:     public int FlightNumber { get; private set; }

6:     public int PostionNumber { get; private set; }

7:     public string Departure { get; private set; }

8:     public string Destination { get; private set; }

9: }

我们简单列举这些属性作为航空公司登机牌上的信息,用法和前面的一样,贴到HumanBase上就行了,说明此人具备登机资格。这里要简单提一下,你可能已经注意到了,在使用BoardingCheckAttribute的时候已经把Attribute省略掉了,不用担心,这样做是对的,因为编译器默认会自己加上然后查找这个属性类的。哦,等一下,我突然想起来他该登哪架飞机呢?显然,在这种需求下,我们的特性还没有起到应有的作用,我们还的做点儿工作,否则乘客面对一张空白的机票一定会很迷茫。

于是,我们必须给这个特性加上构造函数,因为它不仅仅表示登机的资格,还必须包含一些必要的信息才行:

1: public BoardingCheckAttribute(string id, string name, string flightNumber, string positionNumber, string departure, string destination)

2: {

3:     this.ID = id;

4:     this.Name = name;

5:     this.FlightNumber = flightNumber;

6:     this.PositionNumber = positionNumber;

7:     this.Departure = departure;

8:     this.Destination = destination;

9: }

OK,我们的乘客就可以拿到一张正式的登机牌登机了,have a good flight!

1: static void Main(string[] args)

2: {

3:     BoardingCheckAttribute boardingCheck = null;

4:     object[] customAttributes = typeof(HumanPropertyBase).GetCustomAttributes(true);

5:

6:     foreach (var attribute in customAttributes)

7:     {

8:         if (attribute is BoardingCheckAttribute)

9:         {

10:             boardingCheck = attribute as BoardingCheckAttribute;

11:

12:             Console.WriteLine(boardingCheck.Name

13:                         + "'s ID is "

14:                         + boardingCheck.ID

15:                         + ", he/she wants to "

16:                         + boardingCheck.Destination

17:                         + " from "

18:                         + boardingCheck.Departure

19:                         + " by the plane "

20:                         + boardingCheck.FlightNumber

21:                         + ", his/her position is "

22:                         + boardingCheck.PositionNumber

23:                         + ".");

24:         }

25:     }

26:

27:     Console.ReadLine();

28: }

C#: +(特性 ) + Attitude C#(类)前面或者(方法)前面 (中括号)定义的更多相关文章

  1. Python的高级特性8:你真的了解类,对象,实例,方法吗

    Python的高级特性1-7系列是本人从Python2过渡3时写下的一些个人见解(不敢说一定对),接下来的系列主要会以类级为主. 类,对象,实例,方法是几个面向对象的几个基本概念,其实我觉得很多人并不 ...

  2. C#-类 字段 属性 方法 特性

    方法的签名 包括参数个数.参数类型.方法返回值 base和this关键字 1.使用base关键字,就可以使用父类中的字段.属性.方法 2.一旦父类中存在有参数的构造函数,那么子类就必须使用base来覆 ...

  3. .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法

    开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...

  4. Swift互用性: 使用Objective-C特性编写Swift类(Swift 2.0版)-b

    本节包括内容: 继承Objective-C的类(Inheriting from Objective-C Classes) 采用协议(Adopting Protocols) 编写构造器和析构器(Writ ...

  5. C++基础——类继承中方法重载

    一.前言 在上一篇C++基础博文中讨论了C++最基本的代码重用特性——类继承,派生类可以在继承基类元素的同时,添加新的成员和方法.但是没有考虑一种情况:派生类继承下来的方法的实现细节并不一定适合派生类 ...

  6. python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...

  7. Java根类Object的方法说明

    Java中的Object类是所有类的父类,它提供了以下11个方法: public final native Class<?> getClass() public native int ha ...

  8. Python学习笔记之面向对象编程(三)Python类的魔术方法

    python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法.这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能 我主要把它们分为三个部分,下文也是分 ...

  9. Python类总结-字段,方法,属性区别及StaticMethod, Property,私有字段和私有属性

    类包含下列 静态属性 动态属性 静态方法 动态方法 class Province: #静态字段--属于类,调用方法类.字段名 memo = "中国23个省之一" #动态字段--属于 ...

随机推荐

  1. pandas读取csv数据时设置index

    比如读取数据时想把第一列设为index,那么只需要简单的 pd.read_csv("new_wordvecter.csv",index_col=[0]) 这里index_col可以 ...

  2. Beta冲刺——day7

    Beta冲刺--day7 作业链接 Beta冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602134 王龙 ...

  3. REQUIRES_NEW 如果不在一个事务那么自己创建一个事务 如果在一个事务中 自己在这个大事务里面在创建一个子事务 相当于嵌套事务 双层循环那种

    REQUIRES_NEW   如果不在一个事务那么自己创建一个事务 如果在一个事务中 自己在这个大事务里面在创建一个子事务  相当于嵌套事务 双层循环那种 不管是否存在事务,业务方法总会自己开启一个事 ...

  4. 一本通1622Goldbach’s Conjecture

    1622:Goldbach’s Conjecture 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:Ulm Local,题面详见:POJ 2262 ...

  5. gcc 与 glibc 的关系 glibc版本查看

    glibc是什么,以及与gcc的关系?glibc是gnu发布的libc库,也即c运行库.glibc是linux 系统中最底层的api(应用程序开发接口),几乎其它任何的运行库都会倚赖于glibc.gl ...

  6. 【刷题】LOJ 6004 「网络流 24 题」圆桌聚餐

    题目描述 假设有来自 \(n\) 个不同单位的代表参加一次国际会议.每个单位的代表数分别为 \(r_i\) .会议餐厅共有 \(m\) 张餐桌,每张餐桌可容纳 \(c_i\)​​ 个代表就餐. 为了使 ...

  7. 【COGS1752】 BOI2007—摩基亚Mokia

    http://cogs.pro/cogs/problem/problem.php?pid=1752 (题目链接) 题意 给出$n*n$的棋盘,单点修改,矩阵查询. Solution 离线以后CDQ分治 ...

  8. mysql新版本问题

    异常错误:Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.c ...

  9. 【hihocoder】二分·归并排序之逆序对

    描述 在上一回.上上回以及上上上回里我们知道Nettle在玩<艦これ>.经过了一番苦战之后,Nettle又获得了的很多很多的船.这一天Nettle在检查自己的舰队列表:我们可以看到,船默认 ...

  10. Javascript面向

    一.前言 面向对象:专注于由哪一个对象来解决这个问题,编程特点是出现了一个类,从类中拿到对象,由这个对象去解决具体问题.       对于调用者来说,面向过程需要调用者自己去实现各种函数.而面向对象, ...