简介Introduction
07/01/2017
本文内容
C#(读作“See Sharp”)是一种简单易用的新式编程语言,不仅面向对象,还类型安全。C# (pronounced "See Sharp") is a simple, modern, object-oriented, and type-safe programming language. C # 在 C 语言系列中具有其根,并将对 C、c + + 和 Java 程序员立即熟悉。C# has its roots in the C family of languages and will be immediately familiar to C, C++, and Java programmers. C # 由 ECMA 国际标准化为 ecma-334 _ 标准,并通过 ISO/iec 作为 _ iso/iec 23270 standard 进行标准化。C# is standardized by ECMA International as the ECMA-334 _ standard and by ISO/IEC as the _ ISO/IEC 23270 standard. Microsoft 针对 .NET Framework 的 c # 编译器是这两个标准的一致性实现。Microsoft's C# compiler for the .NET Framework is a conforming implementation of both of these standards.
C# 是一种面向对象的语言。不仅如此,C# 还进一步支持 面向组件的 编程。C# is an object-oriented language, but C# further includes support for component-oriented programming. 当代软件设计越来越依赖采用自描述的独立功能包形式的软件组件。Contemporary software design increasingly relies on software components in the form of self-contained and self-describing packages of functionality. 此类组件的关键特征包括:为编程模型提供属性、方法和事件;包含提供组件声明性信息的特性;包含自己的文档。Key to such components is that they present a programming model with properties, methods, and events; they have attributes that provide declarative information about the component; and they incorporate their own documentation. C # 提供了语言构造,以直接支持这些概念,使 c # 成为一种非常自然的语言,用于创建和使用软件组件。C# provides language constructs to directly support these concepts, making C# a very natural language in which to create and use software components.
多个 c # 功能有助于构建强大的持久应用程序: 垃圾回收 _ 会自动回收未使用的对象占用的内存; _异常处理_ 提供了一种结构化的可扩展方法用于错误检测和恢复;以及语言的 _ 类型安全 设计使无法从未初始化的变量中进行读取,将数组索引到其边界之外,或执行未检查的类型转换。Several C# features aid in the construction of robust and durable applications: Garbage collection _ automatically reclaims memory occupied by unused objects; _exception handling_ provides a structured and extensible approach to error detection and recovery; and the _ type-safe design of the language makes it impossible to read from uninitialized variables, to index arrays beyond their bounds, or to perform unchecked type casts.
C# 采用 统一的类型系统。C# has a unified type system. 所有 C# 类型(包括 int 和 double 等基元类型)均继承自一个根 object 类型。All C# types, including primitive types such as int and double, inherit from a single root object type. 因此,所有类型共用一组通用运算,任何类型的值都可以一致地进行存储、传输和处理。Thus, all types share a set of common operations, and values of any type can be stored, transported, and operated upon in a consistent manner. 此外,C# 还支持用户定义的引用类型和值类型,从而支持对象动态分配以及轻量级结构的内嵌式存储。Furthermore, C# supports both user-defined reference types and value types, allowing dynamic allocation of objects as well as in-line storage of lightweight structures.
为了确保 C# 程序和库能够持续兼容,C# 设计非常注重 版本控制。To ensure that C# programs and libraries can evolve over time in a compatible manner, much emphasis has been placed on versioning in C#'s design. 许多编程语言很少关注这个问题,因此,当引入新版依赖库时,用这些语言编写的程序会出现更多不必要的中断现象。Many programming languages pay little attention to this issue, and, as a result, programs written in those languages break more often than necessary when newer versions of dependent libraries are introduced. C# 设计中受版本控制加强直接影响的方面包括:单独的 virtual 和 override 修饰符,关于方法重载决策的规则,以及对显式接口成员声明的支持。Aspects of C#'s design that were directly influenced by versioning considerations include the separate virtual and override modifiers, the rules for method overload resolution, and support for explicit interface member declarations.
本章的其余部分介绍了 c # 语言的重要功能。The rest of this chapter describes the essential features of the C# language. 尽管后面的章节介绍了以详细的方式(有时也是数学方式)的规则和例外,但本章采用的是以完整性为代价来提高清晰度和简洁性。Although later chapters describe rules and exceptions in a detail-oriented and sometimes mathematical manner, this chapter strives for clarity and brevity at the expense of completeness. 目的是为读者提供一篇有助于编写早期程序和阅读后续章节的语言。The intent is to provide the reader with an introduction to the language that will facilitate the writing of early programs and the reading of later chapters.
Hello worldHello world
“Hello, World”程序历来都用于介绍编程语言。The "Hello, World" program is traditionally used to introduce a programming language. 下面展示了此程序的 C# 代码:Here it is in C#:
using System;
class Hello
{
static void Main() {
Console.WriteLine("Hello, World");
}
}
C# 源文件的文件扩展名通常为 .cs。C# source files typically have the file extension .cs. 假设 "Hello,World" 程序存储在文件中 hello.cs ,则可以使用命令行通过 Microsoft c # 编译器编译该程序Assuming that the "Hello, World" program is stored in the file hello.cs, the program can be compiled with the Microsoft C# compiler using the command line
csc hello.cs
这会生成一个名为的可执行程序集 hello.exe 。which produces an executable assembly named hello.exe. 此应用程序在运行时生成的输出为The output produced by this application when it is run is
Hello, World
“Hello, World”程序始于引用 System 命名空间的 using 指令。The "Hello, World" program starts with a using directive that references the System namespace. 命名空间提供了一种用于组织 C# 程序和库的分层方法。Namespaces provide a hierarchical means of organizing C# programs and libraries. 命名空间包含类型和其他命名空间。例如,System 命名空间包含许多类型(如程序中引用的 Console 类)和其他许多命名空间(如 IO 和 Collections)。Namespaces contain types and other namespaces—for example, the System namespace contains a number of types, such as the Console class referenced in the program, and a number of other namespaces, such as IO and Collections. 借助引用给定命名空间的 using 指令,可以非限定的方式使用作为相应命名空间成员的类型。A using directive that references a given namespace enables unqualified use of the types that are members of that namespace. 由于使用 using 指令,因此程序可以使用 Console.WriteLine 作为 System.Console.WriteLine 的简写。Because of the using directive, the program can use Console.WriteLine as shorthand for System.Console.WriteLine.
“Hello, World”程序声明的 Hello 类只有一个成员,即 Main 方法。The Hello class declared by the "Hello, World" program has a single member, the method named Main. Main 方法使用 static 修饰符进行声明。The Main method is declared with the static modifier. 实例方法可以使用关键字 this 引用特定的封闭对象实例,而静态方法则可以在不引用特定对象的情况下运行。While instance methods can reference a particular enclosing object instance using the keyword this, static methods operate without reference to a particular object. 按照约定,Main 静态方法是程序的入口点。By convention, a static method named Main serves as the entry point of a program.
程序的输出是由 System 命名空间中 Console 类的 WriteLine 方法生成。The output of the program is produced by the WriteLine method of the Console class in the System namespace. 此类由 .NET Framework 类库提供,默认情况下,由 Microsoft c # 编译器自动引用。This class is provided by the .NET Framework class libraries, which, by default, are automatically referenced by the Microsoft C# compiler. 请注意,c # 本身没有单独的运行时库。Note that C# itself does not have a separate runtime library. 相反,.NET Framework 是 c # 的运行时库。Instead, the .NET Framework is the runtime library of C#.
程序结构Program structure
C # 中的关键组织概念是 *** 程序** 、 *命名空间*、 类型、 成员 和 *程序集*。The key organizational concepts in C# are programs _, _namespaces*, types, members, and *assemblies*. C# 程序由一个或多个源文件组成。C# programs consist of one or more source files. 程序声明类型,而类型则包含成员,并被整理到命名空间中。Programs declare types, which contain members and can be organized into namespaces. 类型示例包括类和接口。Classes and interfaces are examples of types. 成员示例包括字段、方法、属性和事件。Fields, methods, properties, and events are examples of members. 编译完的 C# 程序实际上会打包到程序集中。When C# programs are compiled, they are physically packaged into assemblies. 程序集通常具有文件扩展名 .exe 或 .dll ,具体取决于它们是否实现 *应用程序* 或 _ 库 *。Assemblies typically have the file extension .exe or .dll, depending on whether they implement applications or _*libraries**.
示例The example
using System;
namespace Acme.Collections
{
public class Stack
{
Entry top;
public void Push(object data) {
top = new Entry(top, data);
}
public object Pop() {
if (top == null) throw new InvalidOperationException();
object result = top.data;
top = top.next;
return result;
}
class Entry
{
public Entry next;
public object data;
public Entry(Entry next, object data) {
this.next = next;
this.data = data;
}
}
}
}
在名为的 Stack 命名空间中声明一个名为的类 Acme.Collections 。declares a class named Stack in a namespace called Acme.Collections. 此类的完全限定的名称为 Acme.Collections.Stack。The fully qualified name of this class is Acme.Collections.Stack. 此类包含多个成员:一个 top 字段、两个方法(Push 和 Pop)和一个 Entry 嵌套类。The class contains several members: a field named top, two methods named Push and Pop, and a nested class named Entry. Entry 类还包含三个成员:一个 next 字段、一个 data 字段和一个构造函数。The Entry class further contains three members: a field named next, a field named data, and a constructor. 假定示例的源代码存储在 acme.cs 文件中,以下命令行Assuming that the source code of the example is stored in the file acme.cs, the command line
csc /t:library acme.cs
将示例编译成库(不含 Main 入口点的代码),并生成 acme.dll 程序集。compiles the example as a library (code without a Main entry point) and produces an assembly named acme.dll.
程序集包含以 *中间语言 _ 为形式的可执行代码 (IL) 指令和格式为 _ metadata * 的符号信息。Assemblies contain executable code in the form of Intermediate Language _ (IL) instructions, and symbolic information in the form of _metadata**. 执行前,程序集中的 IL 代码会被 .NET 公共语言运行时的实时 (JIT) 编译器自动转换成处理器专属代码。Before it is executed, the IL code in an assembly is automatically converted to processor-specific code by the Just-In-Time (JIT) compiler of .NET Common Language Runtime.
由于程序集是包含代码和元数据的自描述功能单元,因此无需在 C# 中使用 #include 指令和头文件。Because an assembly is a self-describing unit of functionality containing both code and metadata, there is no need for #include directives and header files in C#. 只需在编译程序时引用特定的程序集,即可在 C# 程序中使用此程序集中包含的公共类型和成员。The public types and members contained in a particular assembly are made available in a C# program simply by referencing that assembly when compiling the program. 例如,此程序使用 acme.dll 程序集中的 Acme.Collections.Stack 类:For example, this program uses the Acme.Collections.Stack class from the acme.dll assembly:
using System;
using Acme.Collections;
class Test
{
static void Main() {
Stack s = new Stack();
s.Push(1);
s.Push(10);
s.Push(100);
Console.WriteLine(s.Pop());
Console.WriteLine(s.Pop());
Console.WriteLine(s.Pop());
}
}
如果程序存储在文件中 test.cs , test.cs 则编译时, acme.dll 可以使用编译器的选项来引用程序集 /r :If the program is stored in the file test.cs, when test.cs is compiled, the acme.dll assembly can be referenced using the compiler's /r option:
csc /r:acme.dll test.cs
这会创建 test.exe 可执行程序集,它将在运行时输出以下内容:This creates an executable assembly named test.exe, which, when run, produces the output:
100
10
1
使用 C#,可以将程序的源文本存储在多个源文件中。C# permits the source text of a program to be stored in several source files. 编译多文件 C# 程序时,可以将所有源文件一起处理,并且源文件可以随意相互引用。从概念上讲,就像是所有源文件在处理前被集中到一个大文件中一样。When a multi-file C# program is compiled, all of the source files are processed together, and the source files can freely reference each other—conceptually, it is as if all the source files were concatenated into one large file before being processed. 在 C# 中,永远都不需要使用前向声明,因为声明顺序无关紧要(除了极少数的例外情况)。Forward declarations are never needed in C# because, with very few exceptions, declaration order is insignificant. C# 并不限制源文件只能声明一种公共类型,也不要求源文件的文件名必须与其中声明的类型相匹配。C# does not limit a source file to declaring only one public type nor does it require the name of the source file to match a type declared in the source file.
类型和变量Types and variables
C # 中有两种类型: *值类型 _ 和 _ 引用类型 *。There are two kinds of types in C#: value types _ and _reference types**. 值类型的变量直接包含数据,而引用类型的变量则存储对数据(称为“对象”)的引用。Variables of value types directly contain their data whereas variables of reference types store references to their data, the latter being known as objects. 对于引用类型,两个变量可以引用同一对象;因此,对一个变量执行的运算可能会影响另一个变量引用的对象。With reference types, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. 借助值类型,每个变量都有自己的数据副本;因此,对一个变量执行的运算不会影响另一个变量(ref 和 out 参数变量除外)。With value types, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other (except in the case of ref and out parameter variables).
C # 的值类型进一步划分为 简单类型 _、 _枚举类型_、 _结构类型_ 和 _可以为 null 的类型_,并且 c # 的引用类型进一步分为 _类类型_、 _接口类型_、 _数组类型*_ 和 _ 委托类型 *。C#'s value types are further divided into simple types _, _enum types_, _struct types_, and _nullable types_, and C#'s reference types are further divided into _class types_, _interface types_, _array types_, and _delegate types**.
下表概述了 c # 的类型系统。The following table provides an overview of C#'s type system.
类别Category
说明Description
值类型Value types
简单类型Simple types
有符号的整型:sbyte、short、int、longSigned integral: sbyte, short, int, long
无符号的整型:byte、ushort、uint、ulongUnsigned integral: byte, ushort, uint, ulong
Unicode 字符:charUnicode characters: char
IEEE 浮点:float、doubleIEEE floating point: float, double
高精度小数:decimalHigh-precision decimal: decimal
布尔:boolBoolean: bool
枚举类型Enum types
格式为 enum E {...} 的用户定义类型User-defined types of the form enum E {...}
结构类型Struct types
格式为 struct S {...} 的用户定义类型User-defined types of the form struct S {...}
可为 null 的类型Nullable types
值为 null 的其他所有值类型的扩展Extensions of all other value types with a null value
引用类型Reference types
课程类型Class types
其他所有类型的最终基类:objectUltimate base class of all other types: object
Unicode 字符串:stringUnicode strings: string
格式为 class C {...} 的用户定义类型User-defined types of the form class C {...}
接口类型Interface types
格式为 interface I {...} 的用户定义类型User-defined types of the form interface I {...}
数组类型Array types
一维和多维,例如 int[] 和 int[,]Single- and multi-dimensional, for example, int[] and int[,]
委托类型Delegate types
格式为的用户定义的类型,例如 delegate int D(...)User-defined types of the form e.g. delegate int D(...)
八个整型类型支持带符号或不带符号格式的 8 位、16 位、32 位和 64 位值。The eight integral types provide support for 8-bit, 16-bit, 32-bit, and 64-bit values in signed or unsigned form.
这两个浮点类型( float 和 double )使用32位单精度和64位双精度 IEEE 754 格式表示。The two floating point types, float and double, are represented using the 32-bit single-precision and 64-bit double-precision IEEE 754 formats.
decimal 类型是适用于财务和货币计算的 128 位数据类型。The decimal type is a 128-bit data type suitable for financial and monetary calculations.
C # 的 bool 类型用于表示布尔值(或的值) true false 。C#'s bool type is used to represent boolean values—values that are either true or false.
C# 使用 Unicode 编码处理字符和字符串。Character and string processing in C# uses Unicode encoding. char 类型表示 UTF-16 代码单元,string 类型表示一系列 UTF-16 代码单元。The char type represents a UTF-16 code unit, and the string type represents a sequence of UTF-16 code units.
下表总结了 c # 的数值类型。The following table summarizes C#'s numeric types.
类别Category
BitsBits
类型Type
范围/精度Range/Precision
有符号整型Signed integral
88
sbyte
-128 ... 127-128...127
1616
short
-32768 ... 32、767-32,768...32,767
3232
int
-2147483648 ... 2,147,483,647-2,147,483,648...2,147,483,647
6464
long
-9223372036854775808 ... 9,223,372,036,854,775,807-9,223,372,036,854,775,808...9,223,372,036,854,775,807
无符号的整型Unsigned integral
88
byte
0 ... 2550...255
1616
ushort
0 ... 65,5350...65,535
3232
uint
0 ... 4、294次、967、0...4,294,967,295
6464
ulong
0 ... 18、446、744、073、为709、551、6150...18,446,744,073,709,551,615
浮点Floating point
3232
float
1.5 × 10 ^ −45到3.4 × 10 ^ 38,7位精度1.5 × 10^−45 to 3.4 × 10^38, 7-digit precision
6464
double
5.0 × 10 ^ −324到1.7 × 10 ^ 308,15位精度5.0 × 10^−324 to 1.7 × 10^308, 15-digit precision
小数Decimal
128128
decimal
1.0 × 10 ^ −28到7.9 × 10 ^ 28,28位精度1.0 × 10^−28 to 7.9 × 10^28, 28-digit precision
C# 程序使用 类型声明 创建新类型。C# programs use type declarations to create new types. 类型声明指定新类型的名称和成员。A type declaration specifies the name and the members of the new type. 用户可定义以下五种 C# 类型:类类型、结构类型、接口类型、枚举类型和委托类型。Five of C#'s categories of types are user-definable: class types, struct types, interface types, enum types, and delegate types.
类类型定义包含数据成员的数据结构, (字段) 和函数成员 (方法、属性和其他) 。A class type defines a data structure that contains data members (fields) and function members (methods, properties, and others). 类类型支持单一继承和多形性,即派生类可以扩展和专门针对基类的机制。Class types support single inheritance and polymorphism, mechanisms whereby derived classes can extend and specialize base classes.
结构类型类似于类类型,因为它表示包含数据成员和函数成员的结构。A struct type is similar to a class type in that it represents a structure with data members and function members. 但是,与类不同的是,结构是值类型,不需要进行堆分配。However, unlike classes, structs are value types and do not require heap allocation. 结构类型不支持用户指定的继承,并且所有结构类型均隐式继承自类型 object。Struct types do not support user-specified inheritance, and all struct types implicitly inherit from type object.
接口类型将协定定义为公共函数成员的命名集。An interface type defines a contract as a named set of public function members. 实现接口的类或结构必须提供接口的函数成员的实现。A class or struct that implements an interface must provide implementations of the interface's function members. 接口可以从多个基接口继承,类或结构可以实现多个接口。An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces.
委托类型表示对具有特定参数列表和返回类型的方法的引用。A delegate type represents references to methods with a particular parameter list and return type. 通过委托,可以将方法视为可分配给变量并可作为参数传递的实体。Delegates make it possible to treat methods as entities that can be assigned to variables and passed as parameters. 委托类似于其他一些语言中的函数指针概念,但与函数指针不同的是,委托不仅面向对象,还类型安全。Delegates are similar to the concept of function pointers found in some other languages, but unlike function pointers, delegates are object-oriented and type-safe.
类、结构、接口和委托类型都支持泛型,因此可以用其他类型参数化。Class, struct, interface and delegate types all support generics, whereby they can be parameterized with other types.
枚举类型是具有已命名常数的不同类型。An enum type is a distinct type with named constants. 每个枚举类型都有一个基础类型,该类型必须是八个整型类型之一。Every enum type has an underlying type, which must be one of the eight integral types. 枚举类型的值集与基础类型的值集相同。The set of values of an enum type is the same as the set of values of the underlying type.
C# 支持任意类型的一维和多维数组。C# supports single- and multi-dimensional arrays of any type. 与上述类型不同,数组类型无需先声明即可使用。Unlike the types listed above, array types do not have to be declared before they can be used. 相反,数组类型是通过在类型名称后面添加方括号构造而成。Instead, array types are constructed by following a type name with square brackets. 例如,是的一维数组,是的二维数组,是的一维数组, int[] int int[,] int int[][] 它是 int 的一维数组。For example, int[] is a single-dimensional array of int, int[,] is a two-dimensional array of int, and int[][] is a single-dimensional array of single-dimensional arrays of int.
可以为 null 的类型也无需声明即可使用。Nullable types also do not have to be declared before they can be used. 对于每个不可以为 null 的值类型 T ,都有一个对应的可以为 null 的类型 T? ,该类型可以保存附加值 null 。For each non-nullable value type T there is a corresponding nullable type T?, which can hold an additional value null. 例如, int? 是一个可以容纳任何32位整数或值的类型 null 。For instance, int? is a type that can hold any 32 bit integer or the value null.
C # 的类型系统是统一的,因此任何类型的值都可以被视为对象。C#'s type system is unified such that a value of any type can be treated as an object. 每种 C# 类型都直接或间接地派生自 object 类类型,而 object 是所有类型的最终基类。Every type in C# directly or indirectly derives from the object class type, and object is the ultimate base class of all types. 只需将值视为类型 object,即可将引用类型的值视为对象。Values of reference types are treated as objects simply by viewing the values as type object. 值类型的值通过执行 装箱 _ 和 _ 取消装箱 操作被视为对象。Values of value types are treated as objects by performing boxing _ and _ unboxing operations. 在以下示例中,int 值被转换成 object,然后又恢复成 int。In the following example, an int value is converted to object and back again to int.
using System;
class Test
{
static void Main() {
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
}
}
当值类型的值转换为类型时 object ,将分配一个对象实例(也称为 "box")来保存值,并将该值复制到该框中。When a value of a value type is converted to type object, an object instance, also called a "box," is allocated to hold the value, and the value is copied into that box. 相反,将 object 引用强制转换为值类型时,会进行检查以确定所引用的对象是否为正确的值类型的框,如果检查成功,则会将框中的值复制出来。Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type, and, if the check succeeds, the value in the box is copied out.
C # 的统一类型系统实际上意味着值类型可以 "按需" 变为对象。C#'s unified type system effectively means that value types can become objects "on demand." 鉴于这种统一性,使用类型 object 的常规用途库可以与引用类型和值类型结合使用。Because of the unification, general-purpose libraries that use type object can be used with both reference types and value types.
C# 有多种 变量,其中包括字段、数组元素、局部变量和参数。There are several kinds of variables in C#, including fields, array elements, local variables, and parameters. 变量表示存储位置,每个变量都有一种类型,用于确定可以在变量中存储的值,如下表所示。Variables represent storage locations, and every variable has a type that determines what values can be stored in the variable, as shown by the following table.
变量类型Type of Variable
可能的内容Possible Contents
不可以为 null 的值类型Non-nullable value type
具有精确类型的值A value of that exact type
可以为 null 的值类型Nullable value type
空值或该精确类型的值A null value or a value of that exact type
object
空引用、对任何引用类型的对象的引用或对任何值类型的装箱值的引用A null reference, a reference to an object of any reference type, or a reference to a boxed value of any value type
类类型Class type
空引用、对该类类型的实例的引用,或对派生自该类类型的类的实例的引用A null reference, a reference to an instance of that class type, or a reference to an instance of a class derived from that class type
接口类型Interface type
空引用、对实现接口类型的类类型的实例的引用,或对实现接口类型的值类型的装箱值的引用A null reference, a reference to an instance of a class type that implements that interface type, or a reference to a boxed value of a value type that implements that interface type
数组类型Array type
空引用、对该数组类型的实例的引用,或对兼容的数组类型实例的引用A null reference, a reference to an instance of that array type, or a reference to an instance of a compatible array type
委托类型Delegate type
空引用或对该委托类型的实例的引用A null reference or a reference to an instance of that delegate type
表达式Expressions
表达式 _ 是从 _操作数_ 和 _运算符*_ 构造的。Expressions _ are constructed from _operands_ and _operators*_. 表达式的运算符指明了向操作数应用的运算。The operators of an expression indicate which operations to apply to the operands. 运算符的示例包括 +、-、_、/ 和 new。Examples of operators include +, -, _, /, and new. 操作数的示例包括文本、字段、局部变量和表达式。Examples of operands include literals, fields, local variables, and expressions.
如果表达式包含多个运算符,则运算符的 *优先级 _ 控制各个运算符的计算顺序。When an expression contains multiple operators, the *precedence _ of the operators controls the order in which the individual operators are evaluated. 例如,表达式 x + y _ z 相当于计算 x + (y * z),因为 * 运算符的优先级高于 + 运算符。For example, the expression x + y _ z is evaluated as x + (y * z) because the * operator has higher precedence than the + operator.
大部分运算符可 重载。Most operators can be overloaded. 借助运算符重载,可以为一个或两个操作数为用户定义类或结构类型的运算指定用户定义运算符实现代码。Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type.
下表总结了 c # 的运算符,并按优先级从高到低的顺序列出了运算符类别。The following table summarizes C#'s operators, listing the operator categories in order of precedence from highest to lowest. 同一类别的运算符具有相等的优先级。Operators in the same category have equal precedence.
类别Category
表达式Expression
说明Description
主Primary
x.m
成员访问Member access
x(...)
方法和委托调用Method and delegate invocation
x[...]
数组和索引器访问Array and indexer access
x++
后递增Post-increment
x--
后递减Post-decrement
new T(...)
对象和委托创建Object and delegate creation
new T(...){...}
使用初始值设定项创建对象Object creation with initializer
new {...}
匿名对象初始值设定项Anonymous object initializer
new T[...]
数组创建Array creation
typeof(T)
获取 T 的 System.Type 对象Obtain System.Type object for T
checked(x)
在已检查的上下文中计算表达式Evaluate expression in checked context
unchecked(x)
在未检查的上下文中计算表达式Evaluate expression in unchecked context
default(T)
获取类型为 T 的默认值Obtain default value of type T
delegate {...}
匿名函数(匿名方法)Anonymous function (anonymous method)
一元Unary
+x
标识Identity
-x
否定Negation
!x
逻辑非Logical negation
~x
按位求反Bitwise negation
++x
前递增Pre-increment
--x
前递减Pre-decrement
(T)x
将 x 显式转换为类型 TExplicitly convert x to type T
await x
异步等待 x 完成Asynchronously wait for x to complete
乘法性的Multiplicative
x * y
乘法Multiplication
x / y
除法Division
x % y
余数Remainder
累加性Additive
x + y
相加、字符串串联、委托组合Addition, string concatenation, delegate combination
x - y
相减、委托移除Subtraction, delegate removal
移位Shift
x << y
左移Shift left
x >> y
右移Shift right
关系和类型测试Relational and type testing
x < y
小于Less than
x > y
大于Greater than
x <= y
小于或等于Less than or equal
x >= y
大于或等于Greater than or equal
x is T
如果 x 是 T,则返回 true;否则,返回 falseReturn true if x is a T, false otherwise
x as T
返回类型为 T 的 x;如果 x 的类型不是 T,则返回 nullReturn x typed as T, or null if x is not a T
等式Equality
x == y
等于Equal
x != y
不等于Not equal
逻辑与Logical AND
x & y
整型按位 AND,布尔型逻辑 ANDInteger bitwise AND, boolean logical AND
逻辑 XORLogical XOR
x ^ y
整型按位 XOR,布尔型逻辑 XORInteger bitwise XOR, boolean logical XOR
逻辑或Logical OR
x | y
整型按位“或”,布尔型逻辑“或”Integer bitwise OR, boolean logical OR
条件“与”Conditional AND
x && y
y仅当 x 为时才计算trueEvaluates y only if x is true
条件“或”Conditional OR
x || y
y仅当 x 为时才计算falseEvaluates y only if x is false
null 合并Null coalescing
x ?? y
如果为,则计算结果为 y x null , x 否则为Evaluates to y if x is null, to x otherwise
条件逻辑Conditional
x ? y : z
如果 x 为 true,则计算 y;如果 x 为 false,则计算 zEvaluates y if x is true, z if x is false
赋值或匿名函数Assignment or anonymous function
x = y
分配Assignment
x op= y
复合赋值;支持的运算符 *= /= %= += -= 为 <<= >>= &= ^=|=Compound assignment; supported operators are *= /= %= += -= <<= >>= &= ^= |=
(T x) => y
匿名函数(lambda 表达式)Anonymous function (lambda expression)
语句Statements
程序操作使用 语句 进行表示。The actions of a program are expressed using statements. C# 支持几种不同的语句,其中许多语句是从嵌入语句的角度来定义的。C# supports several different kinds of statements, a number of which are defined in terms of embedded statements.
使用 代码块,可以在允许编写一个语句的上下文中编写多个语句。A block permits multiple statements to be written in contexts where a single statement is allowed. 代码块是由一系列在分隔符 { 和 } 内编写的语句组成。A block consists of a list of statements written between the delimiters { and }.
声明语句 用于声明局部变量和常量。Declaration statements are used to declare local variables and constants.
表达式语句 用于计算表达式。Expression statements are used to evaluate expressions. 可用作语句的表达式包括:方法调用、使用运算符的对象分配 new 、使用 = 和复合赋值运算符的赋值、使用 ++ and -- 运算符和 await 表达式递增和递减运算。Expressions that can be used as statements include method invocations, object allocations using the new operator, assignments using = and the compound assignment operators, increment and decrement operations using the ++ and -- operators and await expressions.
选择语句 用于根据一些表达式的值从多个可能的语句中选择一个以供执行。Selection statements are used to select one of a number of possible statements for execution based on the value of some expression. 这一类语句包括 if 和 switch 语句。In this group are the if and switch statements.
迭代语句 用于重复执行嵌入语句。Iteration statements are used to repeatedly execute an embedded statement. 这一类语句包括 while、do、for 和 foreach 语句。In this group are the while, do, for, and foreach statements.
跳转语句 用于转移控制权。Jump statements are used to transfer control. 这一类语句包括 break、continue、goto、throw、return 和 yield 语句。In this group are the break, continue, goto, throw, return, and yield statements.
try...catch 语句用于捕获在代码块执行期间发生的异常,try...finally 语句用于指定始终执行的最终代码,无论异常发生与否。The try...catch statement is used to catch exceptions that occur during execution of a block, and the try...finally statement is used to specify finalization code that is always executed, whether an exception occurred or not.
checked和 unchecked 语句用于控制整型算术运算和转换的溢出检查上下文。The checked and unchecked statements are used to control the overflow checking context for integral-type arithmetic operations and conversions.
lock 语句用于获取给定对象的相互排斥锁定,执行语句,然后解除锁定。The lock statement is used to obtain the mutual-exclusion lock for a given object, execute a statement, and then release the lock.
using 语句用于获取资源,执行语句,然后释放资源。The using statement is used to obtain a resource, execute a statement, and then dispose of that resource.
下面是每种语句的示例Below are examples of each kind of statement
局部变量声明Local variable declarations
static void Main() {
int a;
int b = 2, c = 3;
a = 1;
Console.WriteLine(a + b + c);
}
局部常量声明Local constant declaration
static void Main() {
const float pi = 3.1415927f;
const int r = 25;
Console.WriteLine(pi * r * r);
}
表达式语句Expression statement
static void Main() {
int i;
i = 123; // Expression statement
Console.WriteLine(i); // Expression statement
i++; // Expression statement
Console.WriteLine(i); // Expression statement
}
if 语句if statement
static void Main(string[] args) {
if (args.Length == 0) {
Console.WriteLine("No arguments");
}
else {
Console.WriteLine("One or more arguments");
}
}
switch 语句switch statement
static void Main(string[] args) {
int n = args.Length;
switch (n) {
case 0:
Console.WriteLine("No arguments");
break;
case 1:
Console.WriteLine("One argument");
break;
default:
Console.WriteLine("{0} arguments", n);
break;
}
}
while 语句while statement
static void Main(string[] args) {
int i = 0;
while (i < args.Length) {
Console.WriteLine(args[i]);
i++;
}
}
do 语句do statement
static void Main() {
string s;
do {
s = Console.ReadLine();
if (s != null) Console.WriteLine(s);
} while (s != null);
}
for 语句for statement
static void Main(string[] args) {
for (int i = 0; i < args.Length; i++) {
Console.WriteLine(args[i]);
}
}
foreach 语句foreach statement
static void Main(string[] args) {
foreach (string s in args) {
Console.WriteLine(s);
}
}
break 语句break statement
static void Main() {
while (true) {
string s = Console.ReadLine();
if (s == null) break;
Console.WriteLine(s);
}
}
continue 语句continue statement
static void Main(string[] args) {
for (int i = 0; i < args.Length; i++) {
if (args[i].StartsWith("/")) continue;
Console.WriteLine(args[i]);
}
}
goto 语句goto statement
static void Main(string[] args) {
int i = 0;
goto check;
loop:
Console.WriteLine(args[i++]);
check:
if (i < args.Length) goto loop;
}
return 语句return statement
static int Add(int a, int b) {
return a + b;
}
static void Main() {
Console.WriteLine(Add(1, 2));
return;
}
yield 语句yield statement
static IEnumerable Range(int from, int to) {
for (int i = from; i < to; i++) {
yield return i;
}
yield break;
}
static void Main() {
foreach (int x in Range(-10,10)) {
Console.WriteLine(x);
}
}
throw 和 try 语句throw and try statements
static double Divide(double x, double y) {
if (y == 0) throw new DivideByZeroException();
return x / y;
}
static void Main(string[] args) {
try {
if (args.Length != 2) {
throw new Exception("Two numbers required");
}
double x = double.Parse(args[0]);
double y = double.Parse(args[1]);
Console.WriteLine(Divide(x, y));
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
finally {
Console.WriteLine("Good bye!");
}
}
checked 和 unchecked 语句checked and unchecked statements
static void Main() {
int i = int.MaxValue;
checked {
Console.WriteLine(i + 1); // Exception
}
unchecked {
Console.WriteLine(i + 1); // Overflow
}
}
lock 语句lock statement
class Account
{
decimal balance;
public void Withdraw(decimal amount) {
lock (this) {
if (amount > balance) {
throw new Exception("Insufficient funds");
}
balance -= amount;
}
}
}
using 语句using statement
static void Main() {
using (TextWriter w = File.CreateText("test.txt")) {
w.WriteLine("Line one");
w.WriteLine("Line two");
w.WriteLine("Line three");
}
}
类和对象Classes and objects
*类 _ 是 c # 的最基本类型。*Classes _ are the most fundamental of C#'s types. 类是一种数据结构,可在一个单元中就将状态(字段)和操作(方法和其他函数成员)结合起来。A class is a data structure that combines state (fields) and actions (methods and other function members) in a single unit. 类为动态创建的类(也称为 *对象*)*实例* 提供定义。A class provides a definition for dynamically created instances of the class, also known as objects. 类支持 *继承* 和 *多态性*,即 *派生类* 可以扩展和专用化 _ 基类 * 的机制。Classes support inheritance and polymorphism, mechanisms whereby derived classes can extend and specialize _*base classes**.
新类使用类声明进行创建。New classes are created using class declarations. 类声明的开头是标头,指定了类的特性和修饰符、类名、基类(若指定)以及类实现的接口。A class declaration starts with a header that specifies the attributes and modifiers of the class, the name of the class, the base class (if given), and the interfaces implemented by the class. 标头后面是类主体,由在分隔符 { 和 } 内编写的成员声明列表组成。The header is followed by the class body, which consists of a list of member declarations written between the delimiters { and }.
以下是简单类 Point 的声明:The following is a declaration of a simple class named Point:
public class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
类实例是使用 new 运算符进行创建,此运算符为新实例分配内存,调用构造函数来初始化实例,并返回对实例的引用。Instances of classes are created using the new operator, which allocates memory for a new instance, invokes a constructor to initialize the instance, and returns a reference to the instance. 以下语句创建两个 Point 对象,并将对这些对象的引用存储在两个变量中:The following statements create two Point objects and store references to those objects in two variables:
Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);
当不再使用对象时,将自动回收对象占用的内存。The memory occupied by an object is automatically reclaimed when the object is no longer in use. 既没必要,也无法在 C# 中显式解除分配对象。It is neither necessary nor possible to explicitly deallocate objects in C#.
成员Members
类的成员为 *静态成员 _ 或 _ 实例成员 *。The members of a class are either static members _ or _instance members**. 静态成员属于类,而实例成员则属于对象(类实例)。Static members belong to classes, and instance members belong to objects (instances of classes).
下表提供了类可以包含的成员种类的概述。The following table provides an overview of the kinds of members a class can contain.
成员Member
说明Description
常量Constants
与类相关联的常量值Constant values associated with the class
字段Fields
类的常量Variables of the class
方法Methods
类可以执行的计算和操作Computations and actions that can be performed by the class
属性Properties
与读取和写入类的已命名属性相关联的操作Actions associated with reading and writing named properties of the class
索引器Indexers
与将类实例编入索引(像处理数组一样)相关联的操作Actions associated with indexing instances of the class like an array
事件Events
类可以生成的通知Notifications that can be generated by the class
运算符Operators
类支持的转换和表达式运算符Conversions and expression operators supported by the class
构造函数Constructors
初始化类实例或类本身所需的操作Actions required to initialize instances of the class or the class itself
析构函数Destructors
永久放弃类实例前要执行的操作Actions to perform before instances of the class are permanently discarded
类型Types
类声明的嵌套类型Nested types declared by the class
可访问性Accessibility
每个类成员都有关联的可访问性,用于控制能够访问成员的程序文本区域。Each member of a class has an associated accessibility, which controls the regions of program text that are able to access the member. 可访问性有五种可能的形式。There are five possible forms of accessibility. 下表汇总了这些更新。These are summarized in the following table.
辅助功能Accessibility
含义Meaning
public
访问不受限Access not limited
protected
只能访问此类或派生自此类的类Access limited to this class or classes derived from this class
internal
只能访问此程序Access limited to this program
protected internal
只能访问此程序或派生自此类的类Access limited to this program or classes derived from this class
private
只能访问此类Access limited to this class
类型参数Type parameters
类定义可能会按如下方式指定一组类型参数:在类名后面用尖括号括住类型参数名称列表。A class definition may specify a set of type parameters by following the class name with angle brackets enclosing a list of type parameter names. 然后,可以在类声明的主体中使用类型参数来定义类成员。The type parameters can then be used in the body of the class declarations to define the members of the class. 在以下示例中,Pair 的类型参数是 TFirst 和 TSecond:In the following example, the type parameters of Pair are TFirst and TSecond:
public class Pair
{
public TFirst First;
public TSecond Second;
}
声明为采用类型参数的类类型称为泛型类类型。A class type that is declared to take type parameters is called a generic class type. 结构、接口和委托类型也可以是泛型。Struct, interface and delegate types can also be generic.
使用泛型类时,必须为每个类型参数提供类型自变量:When the generic class is used, type arguments must be provided for each of the type parameters:
Pair pair = new Pair { First = 1, Second = "two" };
int i = pair.First; // TFirst is int
string s = pair.Second; // TSecond is string
提供类型参数的泛型类型(如上文所示 Pair )称为构造类型。A generic type with type arguments provided, like Pair above, is called a constructed type.
基类Base classes
类声明可能会按如下方式指定基类:在类名和类型参数后面编写冒号和基类名。A class declaration may specify a base class by following the class name and type parameters with a colon and the name of the base class. 省略基类规范与从 object 类型派生相同。Omitting a base class specification is the same as deriving from type object. 在以下示例中,Point3D 的基类是 Point,Point 的基类是 object:In the following example, the base class of Point3D is Point, and the base class of Point is object:
public class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public class Point3D: Point
{
public int z;
public Point3D(int x, int y, int z): base(x, y) {
this.z = z;
}
}
类继承其基类的成员。A class inherits the members of its base class. 继承意味着类隐式包含其基类的所有成员(实例和静态构造函数除外)和基类的析构函数。Inheritance means that a class implicitly contains all members of its base class, except for the instance and static constructors, and the destructors of the base class. 派生类可以其继承的类添加新成员,但无法删除继承成员的定义。A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member. 在上面的示例中,Point3D 从 Point 继承了 x 和 y 字段,每个 Point3D 实例均包含三个字段(x、y 和 z)。In the previous example, Point3D inherits the x and y fields from Point, and every Point3D instance contains three fields, x, y, and z.
可以将类类型隐式转换成其任意基类类型。An implicit conversion exists from a class type to any of its base class types. 因此,类类型的变量可以引用相应类的实例或任意派生类的实例。Therefore, a variable of a class type can reference an instance of that class or an instance of any derived class. 例如,类声明如上,Point 类型的变量可以引用 Point 或 Point3D:For example, given the previous class declarations, a variable of type Point can reference either a Point or a Point3D:
Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);
字段Fields
字段是与类或类实例相关联的变量。A field is a variable that is associated with a class or with an instance of a class.
使用修饰符声明的字段 static 定义 静态字段。A field declared with the static modifier defines a static field. 静态字段只指明一个存储位置。A static field identifies exactly one storage location. 无论创建多少个类实例,永远只有一个静态字段副本。No matter how many instances of a class are created, there is only ever one copy of a static field.
未使用修饰符声明的字段 static 定义 实例字段。A field declared without the static modifier defines an instance field. 每个类实例均包含相应类的所有实例字段的单独副本。Every instance of a class contains a separate copy of all the instance fields of that class.
在以下示例中,每个 Color 类实例均包含 r、g 和 b 实例字段的单独副本,但分别只包含 Black、White、Red、Green 和 Blue 静态字段的一个副本:In the following example, each instance of the Color class has a separate copy of the r, g, and b instance fields, but there is only one copy of the Black, White, Red, Green, and Blue static fields:
public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);
private byte r, g, b;
public Color(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
}
}
如上面的示例所示,可以使用 readonly 修饰符声明 只读字段。As shown in the previous example, read-only fields may be declared with a readonly modifier. 对字段的赋值 readonly 只能作为字段声明的一部分出现,或者出现在同一类的构造函数中。Assignment to a readonly field can only occur as part of the field's declaration or in a constructor in the same class.
方法Methods
*方法 _ 是实现可由对象或类执行的计算或操作的成员。A *method _ is a member that implements a computation or action that can be performed by an object or class. 通过类访问 *静态方法*。Static methods are accessed through the class. _ 实例方法* 可通过类的实例进行访问。_ Instance methods* are accessed through instances of the class.
方法具有 (可能为空的 *parameters) 列表(表示传递给方法的值或变量引用)和一个 _ 返回类型 * (指定方法计算并返回的值的类型)。Methods have a (possibly empty) list of parameters _, which represent values or variable references passed to the method, and a _return type**, which specifies the type of the value computed and returned by the method. 如果不返回值,则为方法的返回类型 void 。A method's return type is void if it does not return a value.
方法可能也包含一组类型参数,必须在调用方法时指定类型自变量,这一点与类型一样。Like types, methods may also have a set of type parameters, for which type arguments must be specified when the method is called. 与类型不同的是,通常可以根据方法调用的自变量推断出类型自变量,无需显式指定。Unlike types, the type arguments can often be inferred from the arguments of a method call and need not be explicitly given.
在声明方法的类中,方法的 签名 必须是唯一的。The signature of a method must be unique in the class in which the method is declared. 方法签名包含方法名称、类型参数数量及其参数的数量、修饰符和类型。The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. 方法签名不包含返回类型。The signature of a method does not include the return type.
参数Parameters
参数用于将值或变量引用传递给方法。Parameters are used to pass values or variable references to methods. 方法参数从调用方法时指定的 自变量 中获取其实际值。The parameters of a method get their actual values from the arguments that are specified when the method is invoked. 有四类参数:值参数、引用参数、输出参数和参数数组。There are four kinds of parameters: value parameters, reference parameters, output parameters, and parameter arrays.
值参数 用于传递输入参数。A value parameter is used for input parameter passing. 值参数对应于局部变量,从为其传递的自变量中获取初始值。A value parameter corresponds to a local variable that gets its initial value from the argument that was passed for the parameter. 修改值参数不会影响为其传递的自变量。Modifications to a value parameter do not affect the argument that was passed for the parameter.
可以指定默认值,从而省略相应的自变量,这样值参数就是可选的。Value parameters can be optional, by specifying a default value so that corresponding arguments can be omitted.
引用参数 用于传递输入和输出参数。A reference parameter is used for both input and output parameter passing. 为引用参数传递的自变量必须是变量,并且在方法执行期间,引用参数指明的存储位置与自变量相同。The argument passed for a reference parameter must be a variable, and during execution of the method, the reference parameter represents the same storage location as the argument variable. 引用参数使用 ref 修饰符进行声明。A reference parameter is declared with the ref modifier. 下面的示例展示了如何使用 ref 参数。The following example shows the use of ref parameters.
using System;
class Test
{
static void Swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}
static void Main() {
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine("{0} {1}", i, j); // Outputs "2 1"
}
}
输出参数 用于传递输出参数。An output parameter is used for output parameter passing. 输出参数与引用参数类似,不同之处在于,调用方提供的自变量的初始值并不重要。An output parameter is similar to a reference parameter except that the initial value of the caller-provided argument is unimportant. 输出参数使用 out 修饰符进行声明。An output parameter is declared with the out modifier. 下面的示例展示了如何使用 out 参数。The following example shows the use of out parameters.
using System;
class Test
{
static void Divide(int x, int y, out int result, out int remainder) {
result = x / y;
remainder = x % y;
}
static void Main() {
int res, rem;
Divide(10, 3, out res, out rem);
Console.WriteLine("{0} {1}", res, rem); // Outputs "3 1"
}
}
参数数组 允许向方法传递数量不定的自变量。A parameter array permits a variable number of arguments to be passed to a method. 参数数组使用 params 修饰符进行声明。A parameter array is declared with the params modifier. 参数数组只能是方法的最后一个参数,且参数数组的类型必须是一维数组类型。Only the last parameter of a method can be a parameter array, and the type of a parameter array must be a single-dimensional array type. System.Console 类的 Write 和 WriteLine 方法是参数数组用法的典型示例。The Write and WriteLine methods of the System.Console class are good examples of parameter array usage. 它们的声明方式如下。They are declared as follows.
public class Console
{
public static void Write(string fmt, params object[] args) {...}
public static void WriteLine(string fmt, params object[] args) {...}
...
}
在使用参数数组的方法中,参数数组的行为与数组类型的常规参数完全相同。Within a method that uses a parameter array, the parameter array behaves exactly like a regular parameter of an array type. 不过,在调用包含参数数组的方法时,要么可以传递参数数组类型的一个自变量,要么可以传递参数数组的元素类型的任意数量自变量。However, in an invocation of a method with a parameter array, it is possible to pass either a single argument of the parameter array type or any number of arguments of the element type of the parameter array. 在后一种情况中,数组实例会自动创建,并初始化为包含给定的自变量。In the latter case, an array instance is automatically created and initialized with the given arguments. 以下示例:This example
Console.WriteLine("x={0} y={1} z={2}", x, y, z);
等同于编写以下代码:is equivalent to writing the following.
string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);
方法主体和局部变量Method body and local variables
方法的主体指定在调用方法时要执行的语句。A method's body specifies the statements to execute when the method is invoked.
方法主体可以声明特定于方法调用的变量。A method body can declare variables that are specific to the invocation of the method. 此类变量称为 局部变量。Such variables are called local variables. 局部变量声明指定了类型名称、变量名称以及可能的初始值。A local variable declaration specifies a type name, a variable name, and possibly an initial value. 下面的示例声明了初始值为零的局部变量 i 和无初始值的局部变量 j。The following example declares a local variable i with an initial value of zero and a local variable j with no initial value.
using System;
class Squares
{
static void Main() {
int i = 0;
int j;
while (i < 10) {
j = i * i;
Console.WriteLine("{0} x {0} = {1}", i, j);
i = i + 1;
}
}
}
C# 要求必须先 明确赋值 局部变量,然后才能获取其值。C# requires a local variable to be definitely assigned before its value can be obtained. 例如,如果上面的 i 声明未包含初始值,那么编译器会在后面使用 i 时报告错误,因为在后面使用时 i 不会在程序中进行明确赋值。For example, if the declaration of the previous i did not include an initial value, the compiler would report an error for the subsequent usages of i because i would not be definitely assigned at those points in the program.
方法可以使用 return 语句将控制权返回给调用方。A method can use return statements to return control to its caller. 在返回 void 的方法中,return 语句无法指定表达式。In a method returning void, return statements cannot specify an expression. 在返回非的方法中 void , return 语句必须包含用于计算返回值的表达式。In a method returning non-void, return statements must include an expression that computes the return value.
静态和实例方法Static and instance methods
使用 static 修饰符声明的方法是静态方法。A method declared with a static modifier is a static method. 静态方法不对特定的实例起作用,只能直接访问静态成员。A static method does not operate on a specific instance and can only directly access static members.
未使用 static 修饰符声明的方法是实例方法。A method declared without a static modifier is an instance method. 实例方法对特定的实例起作用,并能够访问静态和实例成员。An instance method operates on a specific instance and can access both static and instance members. 其中调用实例方法的实例可以作为 this 显式访问。The instance on which an instance method was invoked can be explicitly accessed as this. 在静态方法中引用 this 会生成错误。It is an error to refer to this in a static method.
以下 Entity 类包含静态和实例成员。The following Entity class has both static and instance members.
class Entity
{
static int nextSerialNo;
int serialNo;
public Entity() {
serialNo = nextSerialNo++;
}
public int GetSerialNo() {
return serialNo;
}
public static int GetNextSerialNo() {
return nextSerialNo;
}
public static void SetNextSerialNo(int value) {
nextSerialNo = value;
}
}
每个 Entity 实例均有一个序列号(很可能包含此处未显示的其他一些信息)。Each Entity instance contains a serial number (and presumably some other information that is not shown here). Entity 构造函数(类似于实例方法)将新实例初始化为包含下一个可用的序列号。The Entity constructor (which is like an instance method) initializes the new instance with the next available serial number. 由于构造函数是实例成员,因此可以访问 serialNo 实例字段和 nextSerialNo 静态字段。Because the constructor is an instance member, it is permitted to access both the serialNo instance field and the nextSerialNo static field.
GetNextSerialNo 和 SetNextSerialNo 静态方法可以访问 nextSerialNo 静态字段,但如果直接访问 serialNo 实例字段,则会生成错误。The GetNextSerialNo and SetNextSerialNo static methods can access the nextSerialNo static field, but it would be an error for them to directly access the serialNo instance field.
下例显示了 Entity 类的用法。The following example shows the use of the Entity class.
using System;
class Test
{
static void Main() {
Entity.SetNextSerialNo(1000);
Entity e1 = new Entity();
Entity e2 = new Entity();
Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"
Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"
Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"
}
}
请注意,SetNextSerialNo 和 GetNextSerialNo 静态方法是在类中调用,而 GetSerialNo 实例方法则是在类实例中调用。Note that the SetNextSerialNo and GetNextSerialNo static methods are invoked on the class whereas the GetSerialNo instance method is invoked on instances of the class.
虚方法、重写方法和抽象方法Virtual, override, and abstract methods
当实例方法声明包含修饰符时 virtual ,该方法被称为 *虚拟方法 _。When an instance method declaration includes a virtual modifier, the method is said to be a *virtual method _. 当不 virtual 存在修饰符时,此方法被称为 _ 非虚方法 *。When no virtual modifier is present, the method is said to be a _*non-virtual method**.
调用虚方法时,调用发生的实例的 *运行时类型 _ 将确定要调用的实际方法实现。When a virtual method is invoked, the *run-time type _ of the instance for which that invocation takes place determines the actual method implementation to invoke. 在非虚拟方法调用中,实例的 _ 编译时类型* 是确定因素。In a nonvirtual method invocation, the _ compile-time type* of the instance is the determining factor.
可以在派生类中 重写 虚方法。A virtual method can be overridden in a derived class. 当实例方法声明包含修饰符时 override ,此方法将重写具有相同签名的继承的虚方法。When an instance method declaration includes an override modifier, the method overrides an inherited virtual method with the same signature. 但如果虚方法声明中引入新方法,重写方法声明通过提供相应方法的新实现代码,专门针对现有的继承虚方法。Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method.
抽象 方法是无实现的虚方法。An abstract method is a virtual method with no implementation. 抽象方法使用修饰符进行声明 abstract ,只允许在也声明的类中使用 abstract 。An abstract method is declared with the abstract modifier and is permitted only in a class that is also declared abstract. 必须在所有非抽象派生类中重写抽象方法。An abstract method must be overridden in every non-abstract derived class.
下面的示例声明了一个抽象类 Expression,用于表示表达式树节点;还声明了三个派生类(Constant、VariableReference 和 Operation),用于实现常量、变量引用和算术运算的表达式树节点。The following example declares an abstract class, Expression, which represents an expression tree node, and three derived classes, Constant, VariableReference, and Operation, which implement expression tree nodes for constants, variable references, and arithmetic operations. (这类似于,但不与 表达式树类型 中引入的表达式树类型混淆) 。(This is similar to, but not to be confused with the expression tree types introduced in Expression tree types).
using System;
using System.Collections;
public abstract class Expression
{
public abstract double Evaluate(Hashtable vars);
}
public class Constant: Expression
{
double value;
public Constant(double value) {
this.value = value;
}
public override double Evaluate(Hashtable vars) {
return value;
}
}
public class VariableReference: Expression
{
string name;
public VariableReference(string name) {
this.name = name;
}
public override double Evaluate(Hashtable vars) {
object value = vars[name];
if (value == null) {
throw new Exception("Unknown variable: " + name);
}
return Convert.ToDouble(value);
}
}
public class Operation: Expression
{
Expression left;
char op;
Expression right;
public Operation(Expression left, char op, Expression right) {
this.left = left;
this.op = op;
this.right = right;
}
public override double Evaluate(Hashtable vars) {
double x = left.Evaluate(vars);
double y = right.Evaluate(vars);
switch (op) {
case '+': return x + y;
case '-': return x - y;
case '*': return x * y;
case '/': return x / y;
}
throw new Exception("Unknown operator");
}
}
上面的四个类可用于进行算术表达式建模。The previous four classes can be used to model arithmetic expressions. 例如,使用这些类的实例,可以按如下方式表示表达式 x + 3。For example, using instances of these classes, the expression x + 3 can be represented as follows.
Expression e = new Operation(
new VariableReference("x"),
'+',
new Constant(3));
调用 Expression 实例的 Evaluate 方法可以计算给定的表达式并生成 double 值。The Evaluate method of an Expression instance is invoked to evaluate the given expression and produce a double value. 此方法采用作为参数的 Hashtable ,其中包含变量名称 (作为项的键) 和值 (为项) 的值。The method takes as an argument a Hashtable that contains variable names (as keys of the entries) and values (as values of the entries). Evaluate方法是一个虚拟抽象方法,这意味着非抽象派生类必须重写它以提供实际实现。The Evaluate method is a virtual abstract method, meaning that non-abstract derived classes must override it to provide an actual implementation.
Constant 的 Evaluate 实现代码只返回存储的常量。A Constant's implementation of Evaluate simply returns the stored constant. 的 VariableReference 实现查找哈希表中的变量名并返回结果值。A VariableReference's implementation looks up the variable name in the hashtable and returns the resulting value. Operation 实现代码先计算左右操作数(以递归方式调用其 Evaluate 方法),然后执行给定的算术运算。An Operation's implementation first evaluates the left and right operands (by recursively invoking their Evaluate methods) and then performs the given arithmetic operation.
以下程序使用 Expression 类根据不同的 x 和 y 值计算表达式 x * (y + 2)。The following program uses the Expression classes to evaluate the expression x * (y + 2) for different values of x and y.
using System;
using System.Collections;
class Test
{
static void Main() {
Expression e = new Operation(
new VariableReference("x"),
'*',
new Operation(
new VariableReference("y"),
'+',
new Constant(2)
)
);
Hashtable vars = new Hashtable();
vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // Outputs "21"
vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // Outputs "16.5"
}
}
方法重载Method overloading
方法 *重载 _ 允许同一类中的多个方法具有相同的名称,只要它们具有唯一的签名。Method *overloading _ permits multiple methods in the same class to have the same name as long as they have unique signatures. 在编译重载方法的调用时,编译器使用 _ 重载决策* 来确定要调用的特定方法。When compiling an invocation of an overloaded method, the compiler uses _ overload resolution* to determine the specific method to invoke. 重载决策查找与自变量最匹配的方法;如果找不到最佳匹配项,则会报告错误。Overload resolution finds the one method that best matches the arguments or reports an error if no single best match can be found. 下面的示例展示了重载决策的实际工作方式。The following example shows overload resolution in effect. Main 方法中每个调用的注释指明了实际调用的方法。The comment for each invocation in the Main method shows which method is actually invoked.
class Test
{
static void F() {
Console.WriteLine("F()");
}
static void F(object x) {
Console.WriteLine("F(object)");
}
static void F(int x) {
Console.WriteLine("F(int)");
}
static void F(double x) {
Console.WriteLine("F(double)");
}
static void F(T x) {
Console.WriteLine("F(T)");
}
static void F(double x, double y) {
Console.WriteLine("F(double, double)");
}
static void Main() {
F(); // Invokes F()
F(1); // Invokes F(int)
F(1.0); // Invokes F(double)
F("abc"); // Invokes F(object)
F((double)1); // Invokes F(double)
F((object)1); // Invokes F(object)
F(1); // Invokes F(T)
F(1, 1); // Invokes F(double, double)
}
}
如示例所示,可以随时将自变量显式转换成确切的参数类型,并/或显式提供类型自变量,从而选择特定的方法。As shown by the example, a particular method can always be selected by explicitly casting the arguments to the exact parameter types and/or explicitly supplying type arguments.
其他函数成员Other function members
包含可执行代码的成员统称为类的 函数成员。Members that contain executable code are collectively known as the function members of a class. 上一部分介绍了作为主要函数成员类型的方法。The preceding section describes methods, which are the primary kind of function members. 本节介绍 c # 支持的其他类型的函数成员:构造函数、属性、索引器、事件、运算符和析构函数。This section describes the other kinds of function members supported by C#: constructors, properties, indexers, events, operators, and destructors.
下面的代码演示了一个名为 List 的泛型类,该类实现对象的可扩充列表。The following code shows a generic class called List, which implements a growable list of objects. 此类包含最常见类型函数成员的多个示例。The class contains several examples of the most common kinds of function members.
public class List {
// Constant...
const int defaultCapacity = 4;
// Fields...
T[] items;
int count;
// Constructors...
public List(int capacity = defaultCapacity) {
items = new T[capacity];
}
// Properties...
public int Count {
get { return count; }
}
public int Capacity {
get {
return items.Length;
}
set {
if (value < count) value = count;
if (value != items.Length) {
T[] newItems = new T[value];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
}
}
}
// Indexer...
public T this[int index] {
get {
return items[index];
}
set {
items[index] = value;
OnChanged();
}
}
// Methods...
public void Add(T item) {
if (count == Capacity) Capacity = count * 2;
items[count] = item;
count++;
OnChanged();
}
protected virtual void OnChanged() {
if (Changed != null) Changed(this, EventArgs.Empty);
}
public override bool Equals(object other) {
return Equals(this, other as List);
}
static bool Equals(List a, List b) {
if (a == null) return b == null;
if (b == null || a.count != b.count) return false;
for (int i = 0; i < a.count; i++) {
if (!object.Equals(a.items[i], b.items[i])) {
return false;
}
}
return true;
}
// Event...
public event EventHandler Changed;
// Operators...
public static bool operator ==(List a, List b) {
return Equals(a, b);
}
public static bool operator !=(List a, List b) {
return !Equals(a, b);
}
}
构造函数Constructors
C# 支持实例和静态构造函数。C# supports both instance and static constructors. *实例构造函数 _ 是一个成员,用于实现初始化类的实例所需的操作。An *instance constructor _ is a member that implements the actions required to initialize an instance of a class. _ 静态构造函数* 是一个成员,用于实现第一次加载时初始化类本身所需的操作。A _ static constructor* is a member that implements the actions required to initialize a class itself when it is first loaded.
构造函数的声明方式与方法一样,都没有返回类型,且与所含类同名。A constructor is declared like a method with no return type and the same name as the containing class. 如果构造函数声明包含 static 修饰符,则声明的是静态构造函数。If a constructor declaration includes a static modifier, it declares a static constructor. 否则,声明的是实例构造函数。Otherwise, it declares an instance constructor.
可以重载实例构造函数。Instance constructors can be overloaded. 例如,List 类声明两个实例构造函数:一个没有参数,另一个需要使用 int 参数。For example, the List class declares two instance constructors, one with no parameters and one that takes an int parameter. 实例构造函数使用 new 运算符进行调用。Instance constructors are invoked using the new operator. 以下语句 List 使用类的每个构造函数分配两个实例 List 。The following statements allocate two List instances using each of the constructors of the List class.
List list1 = new List();
List list2 = new List(10);
与其他成员不同,实例构造函数不能被继承,且类中只能包含实际已声明的实例构造函数。Unlike other members, instance constructors are not inherited, and a class has no instance constructors other than those actually declared in the class. 如果没有为类提供实例构造函数,则会自动提供不含参数的空实例构造函数。If no instance constructor is supplied for a class, then an empty one with no parameters is automatically provided.
“属性”Properties
*Properties _ 是字段的自然扩展。*Properties _ are a natural extension of fields. 两者都是包含关联类型的已命名成员,用于访问字段和属性的语法也是一样的。Both are named members with associated types, and the syntax for accessing fields and properties is the same. 不过,与字段不同的是,属性不指明存储位置。However, unlike fields, properties do not denote storage locations. 相反,属性具有 _ *访问器**,用于指定读取或写入它们的值时要执行的语句。Instead, properties have _ accessors* that specify the statements to be executed when their values are read or written.
属性的声明方式与字段类似,不同之处在于声明以 get 取值函数和/或分隔符结尾, set 而不是 { 以 } 分号结束。A property is declared like a field, except that the declaration ends with a get accessor and/or a set accessor written between the delimiters { and } instead of ending in a semicolon. 同时具有 get 访问器和访问器的属性 set 是一个 读写属性 _,只有一个 get 访问器的属性是 _只读属性*,并且只有一个 set 访问器的属性是一个 _ *只写属性* *。A property that has both a get accessor and a set accessor is a read-write property _, a property that has only a get accessor is a _read-only property*, and a property that has only a set accessor is a _*write-only property**.
get访问器与具有属性类型返回值的无参数方法相对应。A get accessor corresponds to a parameterless method with a return value of the property type. 除了作为赋值的目标,当在表达式中引用属性时,将 get 调用属性的访问器来计算属性的值。Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property.
set访问器对应于方法,该方法具有一个名为的参数 value ,没有返回类型。A set accessor corresponds to a method with a single parameter named value and no return type. 当属性作为赋值的目标或作为或的操作数进行引用时 ++ -- ,将 set 使用提供新值的自变量调用访问器。When a property is referenced as the target of an assignment or as the operand of ++ or --, the set accessor is invoked with an argument that provides the new value.
List 类声明以下两个属性:Count 和 Capacity(分别为只读和读写)。The List class declares two properties, Count and Capacity, which are read-only and read-write, respectively. 下面的示例展示了如何使用这些属性。The following is an example of use of these properties.
List names = new List();
names.Capacity = 100; // Invokes set accessor
int i = names.Count; // Invokes get accessor
int j = names.Capacity; // Invokes get accessor
类似于字段和方法,C# 支持实例属性和静态属性。Similar to fields and methods, C# supports both instance properties and static properties. 静态属性是用修饰符声明的 static ,实例属性是在没有它的情况下声明的。Static properties are declared with the static modifier, and instance properties are declared without it.
属性的访问器可以是虚的。The accessor(s) of a property can be virtual. 如果属性声明包含 virtual、abstract 或 override 修饰符,则适用于属性的访问器。When a property declaration includes a virtual, abstract, or override modifier, it applies to the accessor(s) of the property.
索引器Indexers
借助 索引器 成员,可以将对象编入索引(像处理数组一样)。An indexer is a member that enables objects to be indexed in the same way as an array. 索引器的声明方式与属性类似,不同之处在于,索引器成员名称格式为 this 后跟在分隔符 [ 和 ] 内写入的参数列表。An indexer is declared like a property except that the name of the member is this followed by a parameter list written between the delimiters [ and ]. 这些参数在索引器的访问器中可用。The parameters are available in the accessor(s) of the indexer. 类似于属性,索引器分为读写、只读和只写索引器,且索引器的访问器可以是虚的。Similar to properties, indexers can be read-write, read-only, and write-only, and the accessor(s) of an indexer can be virtual.
List 类声明一个需要使用 int 参数的读写索引器。The List class declares a single read-write indexer that takes an int parameter. 借助索引器,可以使用 int 值将 List 实例编入索引。The indexer makes it possible to index List instances with int values. 例如For example
List names = new List();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++) {
string s = names[i];
names[i] = s.ToUpper();
}
索引器可以进行重载。也就是说,类可以声明多个索引器,只要其参数的数量或类型不同即可。Indexers can be overloaded, meaning that a class can declare multiple indexers as long as the number or types of their parameters differ.
事件Events
借助 事件 成员,类或对象可以提供通知。An event is a member that enables a class or object to provide notifications. 事件的声明方式与字段类似,区别是事件声明包括 event 关键字,且类型必须是委托类型。An event is declared like a field except that the declaration includes an event keyword and the type must be a delegate type.
在声明事件成员的类中,事件的行为与委托类型的字段完全相同(前提是事件不是抽象的,且不声明访问器)。Within a class that declares an event member, the event behaves just like a field of a delegate type (provided the event is not abstract and does not declare accessors). 字段存储对委托的引用,委托表示已添加到事件的事件处理程序。The field stores a reference to a delegate that represents the event handlers that have been added to the event. 如果不存在任何事件句柄,则字段为 null 。If no event handles are present, the field is null.
List 类声明一个 Changed 事件成员,指明已向列表添加了新项。The List class declares a single event member called Changed, which indicates that a new item has been added to the list. 此 Changed 事件由 OnChanged 虚拟方法引发,该方法首先检查事件是否 null (表示) 没有处理程序。The Changed event is raised by the OnChanged virtual method, which first checks whether the event is null (meaning that no handlers are present). 引发事件的概念恰恰等同于调用由事件表示的委托,因此,没有用于引发事件的特殊语言构造。The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events.
客户端通过 事件处理程序 响应事件。Clients react to events through event handlers. 使用 += 和 -= 运算符分别可以附加和删除事件处理程序。Event handlers are attached using the += operator and removed using the -= operator. 下面的示例展示了如何向 List 的 Changed 事件附加事件处理程序。The following example attaches an event handler to the Changed event of a List.
using System;
class Test
{
static int changeCount;
static void ListChanged(object sender, EventArgs e) {
changeCount++;
}
static void Main() {
List names = new List();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "3"
}
}
对于需要控制事件的基础存储的高级方案,事件声明可以显式提供 add 和 remove 访问器,这在某种程度上与属性的 set 访问器类似。For advanced scenarios where control of the underlying storage of an event is desired, an event declaration can explicitly provide add and remove accessors, which are somewhat similar to the set accessor of a property.
运算符Operators
运算符 是定义向类实例应用特定表达式运算符的含义的成员。An operator is a member that defines the meaning of applying a particular expression operator to instances of a class. 可以定义三种类型的运算符:一元运算符、二元运算符和转换运算符。Three kinds of operators can be defined: unary operators, binary operators, and conversion operators. 所有运算符都必须声明为 public 和 static。All operators must be declared as public and static.
List 类声明两个运算符(operator== 和 operator!=),因此定义了向 List 实例应用这些运算符的表达式的新含义。The List class declares two operators, operator== and operator!=, and thus gives new meaning to expressions that apply those operators to List instances. 具体而言,这些运算符定义的是两个 List 实例的相等性(使用其 Equals 方法比较所包含的每个对象)。Specifically, the operators define equality of two List instances as comparing each of the contained objects using their Equals methods. 下面的示例展示了如何使用 == 运算符比较两个 List 实例。The following example uses the == operator to compare two List instances.
using System;
class Test
{
static void Main() {
List a = new List();
a.Add(1);
a.Add(2);
List b = new List();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b); // Outputs "True"
b.Add(3);
Console.WriteLine(a == b); // Outputs "False"
}
}
第一个 Console.WriteLine 输出 True,因为两个列表包含的对象不仅数量相同,而且值和顺序也相同。The first Console.WriteLine outputs True because the two lists contain the same number of objects with the same values in the same order. 如果 List 未定义 operator==,那么第一个 Console.WriteLine 会输出 False,因为 a 和 b 引用不同的 List 实例。Had List not defined operator==, the first Console.WriteLine would have output False because a and b reference different List instances.
析构函数Destructors
析构函数 是实现析构类的实例所需的操作的成员。A destructor is a member that implements the actions required to destruct an instance of a class. 析构函数不能有参数,它们不能具有可访问性修饰符,也不能被显式调用。Destructors cannot have parameters, they cannot have accessibility modifiers, and they cannot be invoked explicitly. 在垃圾回收过程中会自动调用实例的析构函数。The destructor for an instance is invoked automatically during garbage collection.
垃圾回收器允许使用广泛的纬度来确定何时收集对象和运行析构函数。The garbage collector is allowed wide latitude in deciding when to collect objects and run destructors. 具体而言,析构函数调用的时间是不确定的,析构函数可以在任何线程上执行。Specifically, the timing of destructor invocations is not deterministic, and destructors may be executed on any thread. 出于这些原因和其他原因,类应仅在没有其他任何解决方案可行时才实现析构函数。For these and other reasons, classes should implement destructors only when no other solutions are feasible.
处理对象析构的更好方法是使用 using 语句。The using statement provides a better approach to object destruction.
结构Structs
结构 是可以包含数据成员和函数成员的数据结构,这一点与类一样;与类不同的是,结构是值类型,无需进行堆分配。Like classes, structs are data structures that can contain data members and function members, but unlike classes, structs are value types and do not require heap allocation. 结构类型的变量直接存储结构数据,而类类型的变量存储对动态分配的对象的引用。A variable of a struct type directly stores the data of the struct, whereas a variable of a class type stores a reference to a dynamically allocated object. 结构类型不支持用户指定的继承,并且所有结构类型均隐式继承自类型 object。Struct types do not support user-specified inheritance, and all struct types implicitly inherit from type object.
结构对包含值语义的小型数据结构特别有用。Structs are particularly useful for small data structures that have value semantics. 复数、坐标系中的点或字典中的键值对都是结构的典型示例。Complex numbers, points in a coordinate system, or key-value pairs in a dictionary are all good examples of structs. 对小型数据结构使用结构(而不是类)在应用程序执行的内存分配次数上存在巨大差异。The use of structs rather than classes for small data structures can make a large difference in the number of memory allocations an application performs. 例如,以下程序创建并初始化包含 100 个点的数组。For example, the following program creates and initializes an array of 100 points. 通过将 Point 实现为类,可单独实例化 101 个对象,一个对象用于数组,其他所有对象分别用于 100 个元素。With Point implemented as a class, 101 separate objects are instantiated—one for the array and one each for the 100 elements.
class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
}
}
另一种方法是创建 Point 结构。An alternative is to make Point a struct.
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
现在,仅实例化一个对象(即用于数组的对象),Point 实例存储内嵌在数组中。Now, only one object is instantiated—the one for the array—and the Point instances are stored in-line in the array.
结构构造函数使用 new 运算符进行调用,但这不并表示要分配内存。Struct constructors are invoked with the new operator, but that does not imply that memory is being allocated. 结构构造函数只返回结构值本身(通常在堆栈的临时位置中),并在必要时复制此值,而非动态分配对象并返回对此对象的引用。Instead of dynamically allocating an object and returning a reference to it, a struct constructor simply returns the struct value itself (typically in a temporary location on the stack), and this value is then copied as necessary.
借助类,两个变量可以引用同一对象;因此,对一个变量执行的运算可能会影响另一个变量引用的对象。With classes, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. 借助结构,每个变量都有自己的数据副本;因此,对一个变量执行的运算不会影响另一个变量。With structs, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other. 例如,下面的代码段生成的输出取决于 Point 是类还是结构。For example, the output produced by the following code fragment depends on whether Point is a class or a struct.
Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);
如果 Point 是一个类,则输出为 20 , a 并且 b 引用相同的对象。If Point is a class, the output is 20 because a and b reference the same object. 如果 Point 是一个结构,则输出为, 10 因为的赋值 a b 创建值的副本,而此副本不受对的后续赋值的影响 a.x 。If Point is a struct, the output is 10 because the assignment of a to b creates a copy of the value, and this copy is unaffected by the subsequent assignment to a.x.
以上示例突出显示了结构的两个限制。The previous example highlights two of the limitations of structs. 首先,复制整个结构通常比复制对象引用效率更低,因此通过结构进行的赋值和值参数传递可能比通过引用类型成本更高。First, copying an entire struct is typically less efficient than copying an object reference, so assignment and value parameter passing can be more expensive with structs than with reference types. 其次,除 ref 和 out 参数以外,无法创建对结构的引用,这就表示在很多应用场景中都不能使用结构。Second, except for ref and out parameters, it is not possible to create references to structs, which rules out their usage in a number of situations.
数组Arrays
*Array _ 是一种数据结构,其中包含可通过计算索引访问的多个变量。An *array _ is a data structure that contains a number of variables that are accessed through computed indices. 数组中包含的变量(也称为数组的 *元素* )都属于同一类型,并且此类型称为数组的 _ *元素类型**。The variables contained in an array, also called the elements of the array, are all of the same type, and this type is called the _ element type* of the array.
数组类型是引用类型,声明数组变量只是为引用数组实例预留空间。Array types are reference types, and the declaration of an array variable simply sets aside space for a reference to an array instance. 实际的数组实例是在运行时使用运算符动态创建的 new 。Actual array instances are created dynamically at run-time using the new operator. new 运算指定了新数组实例的长度,然后在此实例的生存期内固定使用这个长度。The new operation specifies the length of the new array instance, which is then fixed for the lifetime of the instance. 数组元素的索引介于 0 到 Length - 1 之间。The indices of the elements of an array range from 0 to Length - 1. new 运算符自动将数组元素初始化为其默认值(例如,所有数值类型的默认值为 0,所有引用类型的默认值为 null)。The new operator automatically initializes the elements of an array to their default value, which, for example, is zero for all numeric types and null for all reference types.
以下示例创建 int 元素数组,初始化此数组,然后打印输出此数组的内容。The following example creates an array of int elements, initializes the array, and prints out the contents of the array.
using System;
class Test
{
static void Main() {
int[] a = new int[10];
for (int i = 0; i < a.Length; i++) {
a[i] = i * i;
}
for (int i = 0; i < a.Length; i++) {
Console.WriteLine("a[{0}] = {1}", i, a[i]);
}
}
}
此示例将创建一个 * 一 维数组 _ 并对其进行操作。This example creates and operates on a *single-dimensional array _. C# 还支持多维数组。C# also supports multi-dimensional arrays. 数组类型的维度的数量(也称为数组类型的 _ *秩**)是一个加在数组类型方括号之间的逗号的数目。The number of dimensions of an array type, also known as the _ rank* of the array type, is one plus the number of commas written between the square brackets of the array type. 下面的示例分配一个一维、二维和三维数组。The following example allocates a one-dimensional, a two-dimensional, and a three-dimensional array.
int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];
a1 数组包含 10 个元素,a2 数组包含 50 个元素 (10 × 5),a3 数组包含 100 个元素 (10 × 5 × 2)。The a1 array contains 10 elements, the a2 array contains 50 (10 × 5) elements, and the a3 array contains 100 (10 × 5 × 2) elements.
数组的元素类型可以是任意类型(包括数组类型)。The element type of an array can be any type, including an array type. 包含数组类型元素的数组有时称为 交错数组,因为元素数组的长度不必全都一样。An array with elements of an array type is sometimes called a jagged array because the lengths of the element arrays do not all have to be the same. 以下示例分配由 int 数组构成的数组:The following example allocates an array of arrays of int:
int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];
第一行创建包含三个元素的数组,每个元素都是 int[] 类型,并且初始值均为 null。The first line creates an array with three elements, each of type int[] and each with an initial value of null. 后面的代码行将这三个元素初始化为引用长度不同的各个数组实例。The subsequent lines then initialize the three elements with references to individual array instances of varying lengths.
通过 new 运算符,可以使用“数组初始值设定项”(在分隔符 { 和 } 内编写的表达式列表)指定数组元素的初始值。The new operator permits the initial values of the array elements to be specified using an array initializer, which is a list of expressions written between the delimiters { and }. 以下示例分配 int[],并将其初始化为包含三个元素。The following example allocates and initializes an int[] with three elements.
int[] a = new int[] {1, 2, 3};
请注意,数组的长度是从和之间的表达式数推断出来 { 的 } 。Note that the length of the array is inferred from the number of expressions between { and }. 局部变量和字段声明可以进一步缩短,这样就不用重新声明数组类型了。Local variable and field declarations can be shortened further such that the array type does not have to be restated.
int[] a = {1, 2, 3};
以上两个示例等同于以下示例:Both of the previous examples are equivalent to the following:
int[] t = new int[3];
t[0] = 1;
t[1] = 2;
t[2] = 3;
int[] a = t;
接口Interfaces
接口 定义了可由类和结构实现的协定。An interface defines a contract that can be implemented by classes and structs. 接口可以包含方法、属性、事件和索引器。An interface can contain methods, properties, events, and indexers. 接口不提供所定义的成员的实现代码,仅指定必须由实现接口的类或结构提供的成员。An interface does not provide implementations of the members it defines—it merely specifies the members that must be supplied by classes or structs that implement the interface.
接口可以采用 多重继承。Interfaces may employ multiple inheritance. 在以下示例中,接口 IComboBox 同时继承自 ITextBox 和 IListBox。In the following example, the interface IComboBox inherits from both ITextBox and IListBox.
interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
interface IListBox: IControl
{
void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}
类和结构可以实现多个接口。Classes and structs can implement multiple interfaces. 在以下示例中,类 EditBox 同时实现 IControl 和 IDataBound。In the following example, the class EditBox implements both IControl and IDataBound.
interface IDataBound
{
void Bind(Binder b);
}
public class EditBox: IControl, IDataBound
{
public void Paint() {...}
public void Bind(Binder b) {...}
}
当类或结构实现特定接口时,此类或结构的实例可以隐式转换成相应的接口类型。When a class or struct implements a particular interface, instances of that class or struct can be implicitly converted to that interface type. 例如For example
EditBox editBox = new EditBox();
IControl control = editBox;
IDataBound dataBound = editBox;
如果已知实例不是静态地实现特定接口,可以使用动态类型显式转换功能。In cases where an instance is not statically known to implement a particular interface, dynamic type casts can be used. 例如,下面的语句使用动态类型强制转换来获取对象的 IControl 和 IDataBound 接口实现。For example, the following statements use dynamic type casts to obtain an object's IControl and IDataBound interface implementations. 由于对象的实际类型为,因此 EditBox 强制转换成功。Because the actual type of the object is EditBox, the casts succeed.
object obj = new EditBox();
IControl control = (IControl)obj;
IDataBound dataBound = (IDataBound)obj;
在以前的 EditBox 类中, Paint 接口中的方法 IControl 和接口中的 Bind 方法 IDataBound 是使用成员实现的 public 。In the previous EditBox class, the Paint method from the IControl interface and the Bind method from the IDataBound interface are implemented using public members. C # 还支持 显式接口成员实现,使用该类或结构可以避免成为成员 public 。C# also supports explicit interface member implementations, using which the class or struct can avoid making the members public. 显式接口成员实现代码是使用完全限定的接口成员名称进行编写。An explicit interface member implementation is written using the fully qualified interface member name. 例如,EditBox 类可以使用显式接口成员实现代码来实现 IControl.Paint 和 IDataBound.Bind 方法,如下所示。For example, the EditBox class could implement the IControl.Paint and IDataBound.Bind methods using explicit interface member implementations as follows.
public class EditBox: IControl, IDataBound
{
void IControl.Paint() {...}
void IDataBound.Bind(Binder b) {...}
}
显式接口成员只能通过接口类型进行访问。Explicit interface members can only be accessed via the interface type. 例如, IControl.Paint EditBox 只有先将 EditBox 引用转换为接口类型,才能调用上一个类提供的实现 IControl 。For example, the implementation of IControl.Paint provided by the previous EditBox class can only be invoked by first converting the EditBox reference to the IControl interface type.
EditBox editBox = new EditBox();
editBox.Paint(); // Error, no such method
IControl control = editBox;
control.Paint(); // Ok
枚举Enums
枚举类型 是包含一组已命名常量的独特值类型。An enum type is a distinct value type with a set of named constants. 下面的示例声明并使用名为 Color 三个常数值、、和的枚举类型 Red Green Blue 。The following example declares and uses an enum type named Color with three constant values, Red, Green, and Blue.
using System;
enum Color
{
Red,
Green,
Blue
}
class Test
{
static void PrintColor(Color color) {
switch (color) {
case Color.Red:
Console.WriteLine("Red");
break;
case Color.Green:
Console.WriteLine("Green");
break;
case Color.Blue:
Console.WriteLine("Blue");
break;
default:
Console.WriteLine("Unknown color");
break;
}
}
static void Main() {
Color c = Color.Red;
PrintColor(c);
PrintColor(Color.Blue);
}
}
每个枚举类型都有一个对应的整型类型,称为枚举类型的 基础类型 。Each enum type has a corresponding integral type called the underlying type of the enum type. 不显式声明基础类型的枚举类型具有基础类型 int 。An enum type that does not explicitly declare an underlying type has an underlying type of int. 枚举类型的存储格式和可能值的范围由其基础类型决定。An enum type's storage format and range of possible values are determined by its underlying type. 枚举类型可以采用的值集不受其枚举成员限制。The set of values that an enum type can take on is not limited by its enum members. 特别是,枚举的基础类型的任何值都可以转换为枚举类型,并且是该枚举类型的一个不同的有效值。In particular, any value of the underlying type of an enum can be cast to the enum type and is a distinct valid value of that enum type.
下面的示例声明一个名为 Alignment 且基础类型为的枚举类型 sbyte 。The following example declares an enum type named Alignment with an underlying type of sbyte.
enum Alignment: sbyte
{
Left = -1,
Center = 0,
Right = 1
}
如前面的示例所示,枚举成员声明可以包含指定成员的值的常量表达式。As shown by the previous example, an enum member declaration can include a constant expression that specifies the value of the member. 每个枚举成员的常数值必须在该枚举的基础类型的范围内。The constant value for each enum member must be in the range of the underlying type of the enum. 当枚举成员声明未显式指定一个值时,如果该成员是枚举) 类型中的第一个成员,则将其值指定为零 (值为零。When an enum member declaration does not explicitly specify a value, the member is given the value zero (if it is the first member in the enum type) or the value of the textually preceding enum member plus one.
可以使用类型强制转换将枚举值转换为整数值,反之亦然。Enum values can be converted to integral values and vice versa using type casts. 例如For example
int i = (int)Color.Blue; // int i = 2;
Color c = (Color)2; // Color c = Color.Blue;
任何枚举类型的默认值都是整数值零,转换为枚举类型。The default value of any enum type is the integral value zero converted to the enum type. 如果变量自动初始化为默认值,则这是给定给枚举类型的变量的值。In cases where variables are automatically initialized to a default value, this is the value given to variables of enum types. 为了使枚举类型的默认值易于使用,文本 0 隐式转换为任何枚举类型。In order for the default value of an enum type to be easily available, the literal 0 implicitly converts to any enum type. 因此,可以运行以下命令。Thus, the following is permitted.
Color c = 0;
委托Delegates
委托类型 表示对具有特定参数列表和返回类型的方法的引用。A delegate type represents references to methods with a particular parameter list and return type. 通过委托,可以将方法视为可分配给变量并可作为参数传递的实体。Delegates make it possible to treat methods as entities that can be assigned to variables and passed as parameters. 委托类似于其他一些语言中的函数指针概念,但与函数指针不同的是,委托不仅面向对象,还类型安全。Delegates are similar to the concept of function pointers found in some other languages, but unlike function pointers, delegates are object-oriented and type-safe.
下面的示例声明并使用 Function 委托类型。The following example declares and uses a delegate type named Function.
using System;
delegate double Function(double x);
class Multiplier
{
double factor;
public Multiplier(double factor) {
this.factor = factor;
}
public double Multiply(double x) {
return x * factor;
}
}
class Test
{
static double Square(double x) {
return x * x;
}
static double[] Apply(double[] a, Function f) {
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
return result;
}
static void Main() {
double[] a = {0.0, 0.5, 1.0};
double[] squares = Apply(a, Square);
double[] sines = Apply(a, Math.Sin);
Multiplier m = new Multiplier(2.0);
double[] doubles = Apply(a, m.Multiply);
}
}
Function 委托类型实例可以引用需要使用 double 自变量并返回 double 值的方法。An instance of the Function delegate type can reference any method that takes a double argument and returns a double value. Apply 方法将给定的 Function 应用于 double[] 的元素,从而返回包含结果的 double[]。The Apply method applies a given Function to the elements of a double[], returning a double[] with the results. 在 Main 方法中,Apply 用于向 double[] 应用三个不同的函数。In the Main method, Apply is used to apply three different functions to a double[].
委托可以引用静态方法(如上面示例中的 Square 或 Math.Sin)或实例方法(如上面示例中的 m.Multiply)。A delegate can reference either a static method (such as Square or Math.Sin in the previous example) or an instance method (such as m.Multiply in the previous example). 引用实例方法的委托还会引用特定对象,通过委托调用实例方法时,该对象会变成调用中的 this。A delegate that references an instance method also references a particular object, and when the instance method is invoked through the delegate, that object becomes this in the invocation.
还可以使用匿名函数创建委托,这些函数是便捷创建的“内联方法”。Delegates can also be created using anonymous functions, which are "inline methods" that are created on the fly. 匿名函数可以查看周围方法的局部变量。Anonymous functions can see the local variables of the surrounding methods. 因此,可以更轻松地编写上面的乘数示例,而无需使用 Multiplier 类:Thus, the multiplier example above can be written more easily without using a Multiplier class:
double[] doubles = Apply(a, (double x) => x * 2.0);
委托的一个有趣且有用的属性是,它不知道也不关心所引用的方法的类;只关心引用的方法是否具有与委托相同的参数和返回类型。An interesting and useful property of a delegate is that it does not know or care about the class of the method it references; all that matters is that the referenced method has the same parameters and return type as the delegate.
属性Attributes
C# 程序中的类型、成员和其他实体支持使用修饰符来控制其行为的某些方面。Types, members, and other entities in a C# program support modifiers that control certain aspects of their behavior. 例如,方法的可访问性是由 public、protected、internal 和 private 修饰符控制。For example, the accessibility of a method is controlled using the public, protected, internal, and private modifiers. C# 整合了这种能力,以便可以将用户定义类型的声明性信息附加到程序实体,并在运行时检索此类信息。C# generalizes this capability such that user-defined types of declarative information can be attached to program entities and retrieved at run-time. 程序通过定义和使用 特性 来指定此附加声明性信息。Programs specify this additional declarative information by defining and using attributes.
以下示例声明了 HelpAttribute 特性,可将其附加到程序实体,以提供指向关联文档的链接。The following example declares a HelpAttribute attribute that can be placed on program entities to provide links to their associated documentation.
using System;
public class HelpAttribute: Attribute
{
string url;
string topic;
public HelpAttribute(string url) {
this.url = url;
}
public string Url {
get { return url; }
}
public string Topic {
get { return topic; }
set { topic = value; }
}
}
所有特性类均派生自 System.Attribute .NET Framework 提供的基类。All attribute classes derive from the System.Attribute base class provided by the .NET Framework. 特性的应用方式为,在相关声明前的方括号内指定特性的名称以及任意自变量。Attributes can be applied by giving their name, along with any arguments, inside square brackets just before the associated declaration. 如果特性的名称以结尾 Attribute ,则引用该属性时可以省略此部分名称。If an attribute's name ends in Attribute, that part of the name can be omitted when the attribute is referenced. 例如,可按如下方法使用 HelpAttribute 特性。For example, the HelpAttribute attribute can be used as follows.
[Help("http://msdn.microsoft.com/.../MyClass.htm")]
public class Widget
{
[Help("http://msdn.microsoft.com/.../MyClass.htm", Topic = "Display")]
public void Display(string text) {}
}
此示例将附加 HelpAttribute 到 Widget 类,并将另一类附加 HelpAttribute 到 Display 类中的方法。This example attaches a HelpAttribute to the Widget class and another HelpAttribute to the Display method in the class. 特性类的公共构造函数控制了将特性附加到程序实体时必须提供的信息。The public constructors of an attribute class control the information that must be provided when the attribute is attached to a program entity. 可以通过引用特性类的公共读写属性(如上面示例对 Topic 属性的引用),提供其他信息。Additional information can be provided by referencing public read-write properties of the attribute class (such as the reference to the Topic property previously).
下面的示例演示如何使用反射在运行时检索给定程序实体的属性信息。The following example shows how attribute information for a given program entity can be retrieved at run-time using reflection.
using System;
using System.Reflection;
class Test
{
static void ShowHelp(MemberInfo member) {
HelpAttribute a = Attribute.GetCustomAttribute(member,
typeof(HelpAttribute)) as HelpAttribute;
if (a == null) {
Console.WriteLine("No help for {0}", member);
}
else {
Console.WriteLine("Help for {0}:", member);
Console.WriteLine(" Url={0}, Topic={1}", a.Url, a.Topic);
}
}
static void Main() {
ShowHelp(typeof(Widget));
ShowHelp(typeof(Widget).GetMethod("Display"));
}
}
通过反射请求获得特定特性时,将调用特性类的构造函数(由程序源提供信息),并返回生成的特性实例。When a particular attribute is requested through reflection, the constructor for the attribute class is invoked with the information provided in the program source, and the resulting attribute instance is returned. 如果是通过属性提供其他信息,那么在特性实例返回前,这些属性会设置为给定值。If additional information was provided through properties, those properties are set to the given values before the attribute instance is returned.