介绍Introduction

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标准)标准化。C# is standardized by ECMA International as the ECMA-334 standard and by ISO/IEC as the ISO/IEC 23270 standard. Microsoft 的C# .NET Framework 编译器是这两个标准的一致性实现。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# 类型(包括 intdouble 等基元类型)均继承自一个根 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# 源文件的文件扩展名通常为 .csC# 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 类)和其他许多命名空间(如 IOCollections)。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. 方法Mainstatic通过修饰符声明的。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.StackThe fully qualified name of this class is Acme.Collections.Stack. 此类包含多个成员:一个 top 字段、两个方法(PushPop)和一个 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)指令形式的可执行代码和元数据形式的符号信息。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.csacme.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. 借助值类型,每个变量都有自己的数据副本;因此,对一个变量执行的运算不会影响另一个变量(refout 参数变量除外)。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 有符号的整型:sbyteshortintlongSigned integral: sbyte, short, int, long
无符号的整型:byteushortuintulongUnsigned integral: byte, ushort, uint, ulong
Unicode 字符:charUnicode characters: char
IEEE 浮点:floatdoubleIEEE 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.

这两个浮点类型( floatdouble)使用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类型用于表示布尔值( truefalse的值)。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 带宽Bits 类型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
DecimalDecimal 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. 结构类型不支持用户指定的继承,并且所有结构类型均隐式继承自类型 objectStruct 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的值类型,都有T?一个对应的可以为 null 的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,然后又恢复成 intIn 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. 运算符的示例包括 +-*/newExamples 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) 获取 TSystem.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 如果 xT,则返回 true;否则,返回 falseReturn true if x is a T, false otherwise
x as T 返回类型为 Tx;如果 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 yx为时才计算trueEvaluates y only if x is true
条件“或”Conditional OR x || y yx为时才计算falseEvaluates y only if x is false
null 合并Null coalescing x ?? y 如果为,则计算结果x为,否则为null x yEvaluates to y if x is null, to x otherwise
条件运算Conditional x ? y : z 如果 xtrue,则计算 y;如果 xfalse,则计算 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. 这一类语句包括 ifswitch 语句。In this group are the if and switch statements.

迭代语句用于重复执行嵌入语句。Iteration statements are used to repeatedly execute an embedded statement. 这一类语句包括 whiledoforforeach 语句。In this group are the while, do, for, and foreach statements.

跳转语句用于转移控制权。Jump statements are used to transfer control. 这一类语句包括 breakcontinuegotothrowreturnyield 语句。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.

checkedunchecked语句用于控制整型算术运算和转换的溢出检查上下文。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 语句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<int> 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);
    }
}

throwtry语句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!");
    }
}

checkedunchecked语句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 the be used in the body of the class declarations to define the members of the class. 在以下示例中,Pair 的类型参数是 TFirstTSecondIn the following example, the type parameters of Pair are TFirst and TSecond:

public class Pair<TFirst,TSecond>
{
    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<int,string> pair = new Pair<int,string> { First = 1, Second = "two" };
int i = pair.First;     // TFirst is int
string s = pair.Second; // TSecond is string

提供类型参数的泛型类型(如上文Pair<int,string>所示)称为构造类型。A generic type with type arguments provided, like Pair<int,string> 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 的基类是 PointPoint 的基类是 objectIn 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. 在上面的示例中,Point3DPoint 继承了 xy 字段,每个 Point3D 实例均包含三个字段(xyz)。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 类型的变量可以引用 PointPoint3DFor 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 类实例均包含 rgb 实例字段的单独副本,但分别只包含 BlackWhiteRedGreenBlue 静态字段的一个副本: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.

方法具有一个(可能为空)参数列表,这些参数表示传递给方法的值或变量引用,后者指定方法所计算和返回的值的类型。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. 类的WriteLine和方法是参数数组用法的很好示例。 Write System.ConsoleThe 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 和无初始值的局部变量 jThe 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.

GetNextSerialNoSetNextSerialNo 静态方法可以访问 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"
    }
}

请注意,SetNextSerialNoGetNextSerialNo 静态方法是在类中调用,而 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,用于表示表达式树节点;还声明了三个派生类(ConstantVariableReferenceOperation),用于实现常量、变量引用和算术运算的表达式树节点。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 + 3For 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.

ConstantEvaluate 实现代码只返回存储的常量。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 类根据不同的 xy 值计算表达式 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>(T x) {
        Console.WriteLine("F<T>(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<int>(1);           // Invokes F<T>(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<T>为的泛型类,该类实现对象的可扩充列表。The following code shows a generic class called List<T>, which implements a growable list of objects. 此类包含最常见类型函数成员的多个示例。The class contains several examples of the most common kinds of function members.

public class List<T> {
    // 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<T>);
    }
    static bool Equals(List<T> a, List<T> 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<T> a, List<T> b) {
        return Equals(a, b);
    }
    public static bool operator !=(List<T> a, List<T> 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<T> 类声明两个实例构造函数:一个没有参数,另一个需要使用 int 参数。For example, the List<T> 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<string>每个构造函数分配两个实例。The following statements allocate two List<string> instances using each of the constructors of the List class.

List<string> list1 = new List<string>();
List<string> list2 = new List<string>(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 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.

访问器对应于方法,该方法具有一个名value为的参数,没有返回类型。 setA 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<T> 类声明以下两个属性:CountCapacity(分别为只读和读写)。The List<T> 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<string> names = new List<string>();
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. 如果属性声明包含 virtualabstractoverride 修饰符,则适用于属性的访问器。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<string> names = new List<string>();
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<T> 类声明一个 Changed 事件成员,指明已向列表添加了新项。The List<T> class declares a single event member called Changed, which indicates that a new item has been added to the list. Changed事件null由虚拟方法引发,该方法首先检查事件是否为(表示没有处理程序)。 OnChangedThe 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<string>Changed 事件附加事件处理程序。The following example attaches an event handler to the Changed event of a List<string>.

using System;

class Test
{
    static int changeCount;

    static void ListChanged(object sender, EventArgs e) {
        changeCount++;
    }

    static void Main() {
        List<string> names = new List<string>();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(changeCount);        // Outputs "3"
    }
}

对于需要控制事件的基础存储的高级方案,事件声明可以显式提供 addremove 访问器,这在某种程度上与属性的 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. 所有运算符都必须声明为 publicstaticAll operators must be declared as public and static.

List<T> 类声明两个运算符(operator==operator!=),因此定义了向 List 实例应用这些运算符的表达式的新含义。The List<T> class declares two operators, operator== and operator!=, and thus gives new meaning to expressions that apply those operators to List instances. 具体而言,运算符将两List<T>个实例的相等性定义为使用其Equals方法来比较每个包含的对象。Specifically, the operators define equality of two List<T> instances as comparing each of the contained objects using their Equals methods. 下面的示例展示了如何使用 == 运算符比较两个 List<int> 实例。The following example uses the == operator to compare two List<int> instances.

using System;

class Test
{
    static void Main() {
        List<int> a = new List<int>();
        a.Add(1);
        a.Add(2);
        List<int> b = new List<int>();
        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<T> 未定义 operator==,那么第一个 Console.WriteLine 会输出 False,因为 ab 引用不同的 List<int> 实例。Had List<T> not defined operator==, the first Console.WriteLine would have output False because a and b reference different List<int> 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. 结构类型不支持用户指定的继承,并且所有结构类型均隐式继承自类型 objectStruct 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是一个类,则输出a20 ,并且b引用相同的对象。If Point is a class, the output is 20 because a and b reference the same object. 如果Point是一个结构,则输出为10b因为的赋值a创建值的副本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. 其次,除 refout 参数以外,无法创建对结构的引用,这就表示在很多应用场景中都不能使用结构。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

数组是一种数据结构,其中包含许多通过计算索引访问的变量。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. 操作指定新数组实例的长度,该长度在实例的生存期内是固定的。 newThe new operation specifies the length of the new array instance, which is then fixed for the lifetime of the instance. 数组元素的索引介于 0Length - 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. 数组类型的维数(亦称为数组类型的)是 1 与数组类型方括号内的逗号数量相加的结果。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[] 类型,并且初始值均为 nullThe 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.

运算符允许使用数组初始值设定项指定数组元素的初始值,数组初始值设定项是在分隔符{}之间编写的表达式的列表。 newThe 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 同时继承自 ITextBoxIListBoxIn 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 同时实现 IControlIDataBoundIn 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. 例如,下面的语句使用动态类型强制转换来获取对象的IControlIDataBound接口实现。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#还支持显式接口成员实现,使用该类或结构可以避免成为成员publicC# 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.PaintIDataBound.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名为三个常数值GreenRed、和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. 不显式声明基础类型的枚举类型具有基础类型intAn 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. 当枚举成员声明未显式指定一个值时,将为该成员赋予值零(如果它是枚举类型中的第一个成员)或上一次的枚举成员的值加1。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. 方法将给定Function的应用于的元素double[]double[]并将返回结果。 ApplyThe 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[].

委托可以引用静态方法(如上面示例中的 SquareMath.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). 引用实例方法的委托还会引用特定对象,通过委托调用实例方法时,该对象会变成调用中的 thisA 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. 例如,方法的可访问性是由 publicprotectedinternalprivate 修饰符控制。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.