[关闭]
@Seymour 2018-07-27T17:08:58.000000Z 字数 16902 阅读 1404

C#简介

C#


C#是一个现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由Ecma和ISO核准认可的。
C#是由Anders Heilsberg和他的团队在,Net框架开发期间开发的。
C#是专为公共语言基础结构(CLI)设计的。CLI由可执行代码和运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言。

C#成为一种广泛应用的专业语言的原因有如下几点:

  1. 现代的、通用的编程语言。
  2. 面向对象。
  3. 面向组件。
  4. 容易学习。
  5. 结构化语言。
  6. 它产生高效率的程序。
  7. 它可以在多种计算机平台上编译。
  8. .Net 框架的一部分。

C# 强大的编程功能

虽然 C# 的构想十分接近于传统高级语言 C 和 C++,是一门面向对象的编程语言,但是它与 Java 非常相似,有许多强大的编程功能,因此得到广大程序员的青睐。

下面列出 C# 一些重要的功能:

C# 环境

本节介绍C#编程所需的工具。C#是.Net框架的一部分,且用于编写.Net应用程序。因此,在讨论运行 C# 程序的可用工具之前,我们先了解一下 C# 与 .Net 框架之间的关系。

.Net 框架(.Net Framework)

.Net 框架是一个创新的平台,能帮您编写出下面类型的应用程序:
- WIndows 应用程序
- Web 应用程序
- Web 服务

.Net框架应用程序是多平台的应用程序。框架的设计方式使它适用于下列各种语言:C#、C++、Visual Basic、JScript、COBOL等等。所有这些语言可以访问框架,彼此之间也可以互相交互。
.Net框架由一个巨大的代码库组成,用于C#等客户端语言。下面列出一些.Net框架的组件:
- 公共语言运行库(Common Language Runtime - CLR)
- .Net框架类库(.Net Framework CLass Library)
- 公共语言规范(Common Language Specification)
- 通用类型系统(Common Type System)
- 元数据(Metadata)和组件(Assemblies)
- Windows 窗体(Windows Forms
- ASP.Net 和 ASP.Net AJAX
- ADO.Net
- Windows 工作流基础(Windows Workflow Foundation - WF)
- Windows 显示基础(Windows Presentation Foundation)
- Windows 通信基础(Windows Communication Foundation - WCF)
- LINQ

C# 的集成开发环境(Integrated Development Environment - IDE)

微软(Microsoft)提供了下列用于 C# 编程的开发工具:

后面两个是免费使用的,可从微软官方网址下载。使用这些工具,我们可以编写各种编写各种C#程序,从简单的命令行应用程序到更复杂的应用程序。您也可以使用基本的文本编辑器(比如Notepad)编写C#源代码文件,并使用命令行编译器(.NET框架的一部分)编译代码为组件。
Visual C# Express 和 Visual Web Developer Express 版本是 Visual Studio 的定制版本,且具有相同的外观和感观。它们保留 Visual Studio 的大部分功能。在本教程中,我们使用的是 Visual C# 2010 Express。
我们可以从 Microsoft Visual Studio上进行下载。它会自动安装在机器上。请注意,我们需要一个可用的网络连接来完成速成版的安装。

在 Linux 或 Mac OS 上编写 C# 程序

虽然 .NET 框架是运行在 Windows 操作系统上,但是也有一些运行于其它操作系统上的版本可供选择。Mono 是 .NET 框架的一个开源版本,它包含了一个 C# 编译器,且可运行于多种操作系统上,比如各种版本的 Linux 和 Mac OS。如需了解更多详情,请访问 Go Mono。
Mono 的目的不仅仅是跨平台地运行微软 .NET 应用程序,而且也为 Linux 开发者提供了更好的开发工具。Mono 可运行在多种操作系统上,包括 Android、BSD、iOS、Linux、OS X、Windows、Solaris 和 UNIX。

C#程序结构

C#Hello World 实例

一个C#程序主要包括以下部分:

  1. using System;
  2. namespace HelloWorldApplication
  3. {
  4. class HelloWorld
  5. {
  6. static void Main(string[] args)
  7. {
  8. /* 我的第一个 C# 程序*/
  9. Console.WriteLine("Hello World");
  10. Console.ReadKey();
  11. }
  12. }
  13. }

当上面的代码被编译和执行时,它会产生下列结果:

Hello World

让我们看一下上面程序的各个部分:

以下几点值得注意:

编译 & 执行 C# 程序

如果您使用 Visual Studio.Net 编译和执行 C# 程序,请按下面的步骤进行:

我们也可以使用命令行代替 Visual Studio IDE 来编译 C# 程序:

C# 基本语法

C# 是一种面向对象的编程语言。在面向对象的程序设计方法中,程序由各种相互交互的对象组成。相同种类的对象通常具有相同的类型,或者说,是在相同的 class 中。

例如,以 Rectangle(矩形)对象为例。它具有 length 和 width 属性。根据设计,它可能需要接受这些属性值、计算面积和显示细节。

让我们来看看一个 Rectangle(矩形)类的实现,并借此讨论 C# 的基本语法:

  1. using System;
  2. namespace RectangleApplication
  3. {
  4. class Rectangle
  5. {
  6. // 成员变量
  7. double length;
  8. double width;
  9. public void Acceptdetails()
  10. {
  11. length = 4.5;
  12. width = 3.5;
  13. }
  14. public double GetArea()
  15. {
  16. return length * width;
  17. }
  18. public void Display()
  19. {
  20. Console.WriteLine("Length: {0}", length);
  21. Console.WriteLine("Width: {0}", width);
  22. Console.WriteLine("Area: {0}", GetArea());
  23. }
  24. }
  25. class ExecuteRectangle
  26. {
  27. static void Main(string[] args)
  28. {
  29. Rectangle r = new Rectangle();
  30. r.Acceptdetails();
  31. r.Display();
  32. Console.ReadLine();
  33. }
  34. }
  35. }

当上面的代码被编译和执行时,它会产生下列结果

Length: 4.5
Width: 3.5
Area: 15.75

using 关键字

在任何 C# 程序中的第一条语句都是:

using System;

using 关键字用于在程序中包含命名空间。一个程序可以包含多个 using 语句。

class 关键字

class 关键字用于声明一个类。

C# 中的注释

注释是用于解释代码。编译器会忽略注释的条目。在 C# 程序中,多行注释以 /* 开始,并以字符 */ 终止,如下所示:

/* This program demonstrates
The basic syntax of C# programming
Language */

单行注释是用 '//' 符号表示。例如:

}//end class Rectangle

成员变量

变量是类的属性或数据成员,用于存储数据。在上面的程序中,Rectangle 类有两个成员变量,名为 length 和 width。

成员函数

函数是一系列执行指定任务的语句。类的成员函数是在类内声明的。我们举例的类 Rectangle 包含了三个成员函数: AcceptDetails、GetArea 和 Display。

实例化一个类

在上面的程序中,类ExecuteRectangle是一个包含Main()方法和实例化Rectangle累的类。

标志符

标志符是用来识别类、变量、函数或任何其他用户定义的项目。在C#中,类的命名必须遵循如下基本原则:

在 C# 中,有些关键字在代码的上下文中有特殊的意义,如 get 和 set,这些被称为上下文关键字(contextual keywords)。

下表列出了 C# 中的保留关键字(Reserved Keywords)和上下文关键字(Contextual Keywords):
保留关键字
| abstract | as | base | bool | break | byte | case | catch | char | checked | class | const | continue | decimal | default | delegate | do | double | else | enum | event | explicit | extern | false | finally | |fixed | float | for | foreach | goto | if | implicit | in | in(generic modifier) | int | interface |internal | is | lock | long | namespace | new | null | object operator | params | private | |protected | public | readonly | ref | return | sbyte | sealed | short | sozeof | stackalloc | static | string | struct | switch | this | throw | true | try | typeof | unit | ulong | unchecked | unsafe | ushort | using | virtual | void | volatile | while |
上下文关键字
| add | alias | ascending | descending | dynamic | from | get |
|global | group | into | join | let | orderby | partial |
|(type) | partial (method) | remove | select | set |

C# 数据类型

在 C# 中,变量分为以下几种类型:

值类型(Value types)
引用类型(Reference types)
指针类型(Pointer types)

值类型(Value types)
值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。

值类型直接包含数据。比如 int、char、float,它们分别存储数字、字符、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。

下表列出了 C# 中可用的值类型:

类型 描述 范围 默认值
bool 布尔值 True 或 False False
char 16 位 Unicode 字符 U +0000 到 U +ffff '\0'
decimal 128 位精确的十进制值 28-29 有效位数 (-7.9 x 1028 到 7.9 x 1028) / 100 到 28 0.0M
double 64 位双精度浮点型 (+/-)5.0 x 10-324 到 (+/-)1.7 x 10308 0.0D
float 32 位单精度浮点型 -3.4 x 1038 到 + 3.4 x 1038 0.0F
int 32 位有符号整数类型 -2,147,483,648 到 2,147,483,647 0
long 64 位有符号整数类型 -923,372,036,854,775,808 到 9,223,372,036,854,775,807 0L
sbyte 8 位有符号整数类型 -32,768 到 32,767 0
short 16 位有符号整数类型 U +0000 到 U +ffff '\0'
uint 布尔值 0 到 4,294,967,295 0 到 4,294,967,295
ulong 64 位无符号整数类型 0 到 18,446,744,073,709,551,615 0
ushort 16 位无符号整数类型 U +0000 到 U +ffff '\0'

如需得到一个类型或一个变量在特定平台上的准确尺寸,可以使用 sizeof 方法。表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。下面举例获取任何机器上 int 类型的存储尺寸:

  1. namespace DataTypeApplication
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. Console.WriteLine("Size of int: {0}", sizeof(int));
  8. Console.ReadLine();
  9. }
  10. }
  11. }

当上面的代码被编译和执行时,它会产生下列结果:

Size of int: 4

引用类型(Reference types)

引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。

换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。

对象(Object)类型

对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。

当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱

object obj;
obj = 100; // 这是装箱

动态(Dynamic)类型

您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。

声明动态类型的语法:

dynamic = value;

例如:

dynamic d = 20;

动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的

字符串(String)类型

字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。

例如:

String str = "runoob.com";

一个 @引号字符串:

@"runoob.com";

C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:

string str = @"C:\Windows";

等价于:

string str = "C:\Windows";

@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。

string str = @"
-->
";

用户自定义引用类型有:class、interface 或 delegate。我们将在以后的章节中讨论这些类型。

指针类型(Pointer types)

指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。

声明指针类型的语法:

type* identifier;

例如:

char* cptr;
int* iptr;

C# 类型转换

类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:

  1. 隐式类型转换 - 这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
  2. 显式类型转换 - 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。
    下面的实例显示了一个显式的类型转换:
  1. namespace TypeConversionApplication
  2. {
  3. class ExplicitConversion
  4. {
  5. static void Main(string[] args)
  6. {
  7. double d = 5673.74;
  8. int i;
  9. // 强制转换 double 为 int
  10. i = (int)d;
  11. Console.WriteLine(i);
  12. Console.ReadKey();
  13. }
  14. }
  15. }

当上面的代码被编译和执行时,它会产生下列结果:

5673

C# 类型转换方法

C# 提供了下列内置的类型转换方法:

序号 方法 & 描述
1 ToBoolean
如果可能的话,把类型转换为布尔型。
2 ToByte
把类型转换为字节类型。
3 ToChar
如果可能的话,把类型转换为单个 Unicode 字符类型。
4 ToDateTime
把类型(整数或字符串类型)转换为 日期-时间 结构。
5 ToDecimal
把浮点型或整数类型转换为十进制类型。
6 ToDouble
把类型转换为双精度浮点型。
7 ToInt16
把类型转换为 16 位整数类型。
8 ToInt32
把类型转换为 32 位整数类型。
9 ToInt64
把类型转换为 64 位整数类型。
10 ToSbyte
把类型转换为有符号字节类型。
11 ToSingle
把类型转换为小浮点数类型。
12 ToString
把类型转换为字符串类型。
13 ToType
把类型转换为指定类型。
14 ToUInt16
把类型转换为 16 位无符号整数类型。
15 ToUInt32
把类型转换为 32 位无符号整数类型。
16 ToUInt64
把类型转换为 64 位无符号整数类型。

下面的实例把不同值的类型转换为字符串类型:

  1. namespace TypeConversionApplication
  2. {
  3. class StringConversion
  4. {
  5. static void Main(string[] args)
  6. {
  7. int i = 75;
  8. float f = 53.005f;
  9. double d = 2345.7652;
  10. bool b = true;
  11. Console.WriteLine(i.ToString());
  12. Console.WriteLine(f.ToString());
  13. Console.WriteLine(d.ToString());
  14. Console.WriteLine(b.ToString());
  15. Console.ReadKey();
  16. }
  17. }
  18. }

当上面的代码被编译和执行时,它会产生下列结果:

75
53.005
2345.7652
True

C# 特性(Attribute)

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

规定特性(Attribute)

规定特性(Attribute)的语法如下:

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

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

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]

其中:

例如:

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
Conditional

这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。

规定该特性的语法如下:

[Conditional(
conditionalSymbol
)]

例如:

[Conditional("DEBUG")]

下面的实例演示了该特性:

  1. #define DEBUG
  2. using System;
  3. using System.Diagnostics;
  4. public class Myclass
  5. {
  6. [Conditional("DEBUG")]
  7. public static void Message(string msg)
  8. {
  9. Console.WriteLine(msg);
  10. }
  11. }
  12. class Test
  13. {
  14. static void function1()
  15. {
  16. Myclass.Message("In Function 1.");
  17. function2();
  18. }
  19. static void function2()
  20. {
  21. Myclass.Message("In Function 2.");
  22. }
  23. public static void Main()
  24. {
  25. Myclass.Message("In Main function.");
  26. function1();
  27. Console.ReadKey();
  28. }
  29. }

当上面的代码被编译和执行时,它会产生下列结果:

In Main function
In Function 1
In Function 2

Obsolete

这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

规定该特性的语法如下:

[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]

其中:

下面的实例演示了该特性:

  1. using System;
  2. public class MyClass
  3. {
  4. [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
  5. static void OldMethod()
  6. {
  7. Console.WriteLine("It is the old method");
  8. }
  9. static void NewMethod()
  10. {
  11. Console.WriteLine("It is the new method");
  12. }
  13. public static void Main()
  14. {
  15. OldMethod();
  16. }
  17. }

当您尝试编译该程序时,编译器会给出一个错误消息说明:

Don't use OldMethod, use NewMethod instead

创建自定义特性(Attribute)

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

创建并使用自定义特性包含四个步骤:

最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一章详细讨论这点。

声明自定义特性

一个新的自定义特性应派生自 System.Attribute 类。例如:

// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

public class DeBugInfo : System.Attribute

在上面的代码中,我们已经声明了一个名为 DeBugInfo 的自定义特性。

构建自定义特性

让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:

我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。

每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:

// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

  1. public class DeBugInfo : System.Attribute
  2. {
  3. private int bugNo;
  4. private string developer;
  5. private string lastReview;
  6. public string message;
  7. public DeBugInfo(int bg, string dev, string d)
  8. {
  9. this.bugNo = bg;
  10. this.developer = dev;
  11. this.lastReview = d;
  12. }
  13. public int BugNo
  14. {
  15. get
  16. {
  17. return bugNo;
  18. }
  19. }
  20. public string Developer
  21. {
  22. get
  23. {
  24. return developer;
  25. }
  26. }
  27. public string LastReview
  28. {
  29. get
  30. {
  31. return lastReview;
  32. }
  33. }
  34. public string Message
  35. {
  36. get
  37. {
  38. return message;
  39. }
  40. set
  41. {
  42. message = value;
  43. }
  44. }
  45. }

应用自定义特性

通过把特性放置在紧接着它的目标之前,来应用该特性:

  1. [DeBugInfo(45, "Solomon", "12/8/2018", Message = "Return type mismatch")]
  2. [DeBugInfo(49, "Solomon", "10/10/2018", Message = "Unused variable")]
  3. class Rectangle
  4. {
  5. // 成员变量
  6. protected double length;
  7. protected double width;
  8. public Rectangle(double l, double w)
  9. {
  10. length = l;
  11. width = w;
  12. }
  13. [DeBugInfo(55, "Solomon", "19/10/2018,
  14. Message = "Return type mismatch")]
  15. public double GetArea()
  16. {
  17. return length * width;
  18. }
  19. [DeBugInfo(56, "Solomon", "19/10/2018)]
  20. public void Display()
  21. {
  22. Console.WriteLine("Length: {0}", length);
  23. Console.WriteLine("Width: {0}", width);
  24. Console.WriteLine("Area: {0}", GetArea());
  25. }
  26. }

C# 反射(Reflection)

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优缺点

优点:

1、反射提高了程序的灵活性和扩展性。
2、降低耦合性,提高自适应能力。
3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

反射(Reflection)的用途

反射(Reflection)有下列用途:

查看元数据

我们已经在上面的章节中提到过,使用反射(Reflection)可以查看特性(attribute)信息。

System.Reflection 类的 MemberInfo

对象需要被初始化,用于发现与类相关的特性(attribute)。为了做到这点,您可以定义目标类的一个对象,如下:

System.Reflection.MemberInfo info = typeof(MyClass);

下面的程序演示了这点:

  1. using System;
  2. [AttributeUsage(AttributeTargets.All)]
  3. public class HelpAttribute : System.Attribute
  4. {
  5. public readonly string Url;
  6. public string Topic // Topic 是一个命名(named)参数
  7. {
  8. get
  9. {
  10. return topic;
  11. }
  12. set
  13. {
  14. topic = value;
  15. }
  16. }
  17. public HelpAttribute(string url) // url 是一个定位(positional)参数
  18. {
  19. this.Url = url;
  20. }
  21. private string topic;
  22. }
  23. [HelpAttribute("Information on the class MyClass")]
  24. class MyClass
  25. {
  26. }
  27. namespace AttributeAppl
  28. {
  29. class Program
  30. {
  31. static void Main(string[] args)
  32. {
  33. System.Reflection.MemberInfo info = typeof(MyClass);
  34. object[] attributes = info.GetCustomAttributes(true);
  35. for (int i = 0; i < attributes.Length; i++)
  36. {
  37. System.Console.WriteLine(attributes[i]);
  38. }
  39. Console.ReadKey();
  40. }
  41. }
  42. }

当上面的代码被编译和执行时,它会显示附加到类 MyClass 上的自定义特性:

HelpAttribute

实例

在本实例中,我们将使用在上一章中创建的 DeBugInfo 特性,并使用反射(Reflection)来读取 Rectangle 类中的元数据。

  1. using System;
  2. using System.Reflection;
  3. namespace BugFixApplication
  4. {
  5. // 一个自定义特性 BugFix 被赋给类及其成员
  6. [AttributeUsage(AttributeTargets.Class |
  7. AttributeTargets.Constructor |
  8. AttributeTargets.Field |
  9. AttributeTargets.Method |
  10. AttributeTargets.Property,
  11. AllowMultiple = true)]
  12. public class DeBugInfo : System.Attribute
  13. {
  14. private int bugNo;
  15. private string developer;
  16. private string lastReview;
  17. public string message;
  18. public DeBugInfo(int bg, string dev, string d)
  19. {
  20. this.bugNo = bg;
  21. this.developer = dev;
  22. this.lastReview = d;
  23. }
  24. public int BugNo
  25. {
  26. get
  27. {
  28. return bugNo;
  29. }
  30. }
  31. public string Developer
  32. {
  33. get
  34. {
  35. return developer;
  36. }
  37. }
  38. public string LastReview
  39. {
  40. get
  41. {
  42. return lastReview;
  43. }
  44. }
  45. public string Message
  46. {
  47. get
  48. {
  49. return message;
  50. }
  51. set
  52. {
  53. message = value;
  54. }
  55. }
  56. }
  57. [DeBugInfo(45, "Solomon", "12/8/2018",
  58. Message = "Return type mismatch")]
  59. [DeBugInfo(49, "Solomnn", "10/10/2018",
  60. Message = "Unused variable")]
  61. class Rectangle
  62. {
  63. // 成员变量
  64. protected double length;
  65. protected double width;
  66. public Rectangle(double l, double w)
  67. {
  68. length = l;
  69. width = w;
  70. }
  71. [DeBugInfo(55, "Solonon", "19/10/2018",
  72. Message = "Return type mismatch")]
  73. public double GetArea()
  74. {
  75. return length * width;
  76. }
  77. [DeBugInfo(56, "Solomon", "19/10/2018")]
  78. public void Display()
  79. {
  80. Console.WriteLine("Length: {0}", length);
  81. Console.WriteLine("Width: {0}", width);
  82. Console.WriteLine("Area: {0}", GetArea());
  83. }
  84. }//end class Rectangle
  85. class ExecuteRectangle
  86. {
  87. static void Main(string[] args)
  88. {
  89. Rectangle r = new Rectangle(4.5, 7.5);
  90. r.Display();
  91. Type type = typeof(Rectangle);
  92. // 遍历 Rectangle 类的特性
  93. foreach (Object attributes in type.GetCustomAttributes(false))
  94. {
  95. DeBugInfo dbi = (DeBugInfo)attributes;
  96. if (null != dbi)
  97. {
  98. Console.WriteLine("Bug no: {0}", dbi.BugNo);
  99. Console.WriteLine("Developer: {0}", dbi.Developer);
  100. Console.WriteLine("Last Reviewed: {0}",
  101. dbi.LastReview);
  102. Console.WriteLine("Remarks: {0}", dbi.Message);
  103. }
  104. }
  105. // 遍历方法特性
  106. foreach (MethodInfo m in type.GetMethods())
  107. {
  108. foreach (Attribute a in m.GetCustomAttributes(true))
  109. {
  110. DeBugInfo dbi = (DeBugInfo)a;
  111. if (null != dbi)
  112. {
  113. Console.WriteLine("Bug no: {0}, for Method: {1}",
  114. dbi.BugNo, m.Name);
  115. Console.WriteLine("Developer: {0}", dbi.Developer);
  116. Console.WriteLine("Last Reviewed: {0}",
  117. dbi.LastReview);
  118. Console.WriteLine("Remarks: {0}", dbi.Message);
  119. }
  120. }
  121. }
  122. Console.ReadLine();
  123. }
  124. }
  125. }

当上面的代码被编译和执行时,它会产生下列结果:

Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Solomon
Last Reviewed: 10/10/2018
Remarks: Unused variable
Bug No: 45
Developer: Solomon
Last Reviewed: 12/8/2018
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Solomon
Last Reviewed: 19/10/2018
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Solomon
Last Reviewed: 19/10/2018
Remarks:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注