程序构建基块 2021/5/7·· Edit Online 上文中介绍的类型是使用以下构建基块生成的:成员*、表达式和语句 成员 ass的成员要么是静态成员,要么是实例成员。静态成员属于类,而实例成员则属于对象(类实例)。 以下列表概述了类可以包含的成员类型。 常量:与类相关联的常量值 字段:与类关联的变量 ●方法类可执行的操作 属性:与读取和写入类的已命名属性相关联的操作 索引器:与捋类实例编入索引(像处理数组一样)相关联的操作 事件:类可以生成的通知 运算符:类支持的转换和表达式运算符 构造函数:初始化类实例或类本身所需的操作 终结器:永久放弃类实例之前执行的操作 类型:类声明的嵌套类型 辅助功能 每个类成员都有关联的可访问性,用于控制能够访问成员的程序文本区域。可访问性有六种可能的形式。以下 内容对访问修饰符进行了汇总。 public: t访问不受限制 · private:访问仅限于此类。 protected:访问仅限于此类或派生自此类的类 · internal:仅可访间当前程序集(e或.dn1)。 protected internal:仅可访问此类、从此类中派生的类,或者同一程序集中的类。 private protected:仅可访问此类或同一程序集中从此类中派生的类。 字段 字段是与类或类实例相关联的变量。 使用静态修饰符声明的字段定义的是静态字段。静态字段只指明一个存储位置。无论创建多少个类实例,永远 只有一个静态字段副本。 不使用静态修饰符声明的字段定义的是实例字段。每个类实例均包含相应类的所有实例字段的单独副本。 在以下示例中,每个col类实例均包含R、6和B实例字段的单独副本,但只包含 Black、 White、Red Gren和Blue静态字段的一个副本
程序构建基块 2021/5/7 • • Edit Online 成员 辅助功能 字段 上文中介绍的类型是使用以下构建基块生成的:成员*_、表达式和语句_*。 class 的成员要么是静态成员,要么是实例成员。 静态成员属于类,而实例成员则属于对象(类实例)。 以下列表概述了类可以包含的成员类型。 常量:与类相关联的常量值 字段:与类关联的变量 方法:类可执行的操作 属性:与读取和写入类的已命名属性相关联的操作 索引器:与将类实例编入索引(像处理数组一样)相关联的操作 事件:类可以生成的通知 运算符:类支持的转换和表达式运算符 构造函数:初始化类实例或类本身所需的操作 终结器:永久放弃类实例之前执行的操作 类型:类声明的嵌套类型 每个类成员都有关联的可访问性,用于控制能够访问成员的程序文本区域。 可访问性有六种可能的形式。 以下 内容对访问修饰符进行了汇总。 public :访问不受限制。 private :访问仅限于此类。 protected :访问仅限于此类或派生自此类的类。 internal :仅可访问当前程序集( .exe 或 .dll )。 protected internal :仅可访问此类、从此类中派生的类,或者同一程序集中的类。 private protected :仅可访问此类或同一程序集中从此类中派生的类。 字段 是与类或类实例相关联的变量。 使用静态修饰符声明的字段定义的是静态字段。 静态字段只指明一个存储位置。 无论创建多少个类实例,永远 只有一个静态字段副本。 不使用静态修饰符声明的字段定义的是实例字段。 每个类实例均包含相应类的所有实例字段的单独副本。 在以下示例中,每个 Color 类实例均包含 R 、 G 和 B 实例字段的单独副本,但只包含 Black 、 White 、 Red 、 Green 和 Blue 静态字段的一个副本:
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); public byte R; public byte G public byte B; public Color(byte r, byte g, byte b) rgb 如上面的示例所示,可以使用 readonly修饰符声明只读字段。只能在字段声明期间或在同一个类的构造函数 中向只读字段赋值。 方法 方法是实现对象或类可执行的计算或操作的成员。静态方法是通过类进行访问。实例方法是通过类实例进行 访问 方法可能包含一个参数列表,这些参数表示传递给方法的值或变量引用。方法具有返回类型,它用于指定方法计 算和返回的值的类型。如果方法未返回值,则它的返回类型为void。 方法可能也包含一组类型参数,必须在调用方法时指定类型自变量,这一点与类型一样。与类型不同的是,通常 可以根据方法调用的自变量推断出类型自变量,无需显式指定 在声明方法的类中,方法的签名必须是唯一的。方法签名包含方法名称、类型参数数量及其参数的数量、修饰 符和类型。方法签名不包含返回类型。 当方法主体是单个表达式时,可使用紧凑表达式格式定义方法,如下例中所示 public override string ToString()=>"This is an object 参数 参数用于捋值或变量引用传递给方法。方法参数从调用方法时指定的自变量中获取其实际值。有四类参数:值 参数、引用参数、输出参数和参数数组。 值参数用于传递输入自变量。值参数对应于局部变量,从为其传递的自变量中获取初始值。修改值形参不会影 响为其传递的实参。 可以指定默认值,从而省略相应的自变量,这样值参数就是可选的。 引用参数用于按引用传递自变量。为引用参数传递的自变量必须是一个带有明确值的变量。在方法执行期间 引用参数指出的存储位置与自变量相同。引用参数使用ref修饰符进行声明。下面的示例展示了如何使用 ref参数
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); public byte R; public byte G; public byte B; public Color(byte r, byte g, byte b) { R = r; G = g; B = b; } } 方法 public override string ToString() => "This is an object"; 参数 如上面的示例所示,可以使用 readonly 修饰符声明 只读字段。 只能在字段声明期间或在同一个类的构造函数 中向只读字段赋值。 方法 是实现对象或类可执行的计算或操作的成员。 静态方法 是通过类进行访问。 实例方法 是通过类实例进行 访问。 方法可能包含一个参数列表,这些参数表示传递给方法的值或变量引用。 方法具有返回类型,它用于指定方法计 算和返回的值的类型。 如果方法未返回值,则它的返回类型为 void 。 方法可能也包含一组类型参数,必须在调用方法时指定类型自变量,这一点与类型一样。 与类型不同的是,通常 可以根据方法调用的自变量推断出类型自变量,无需显式指定。 在声明方法的类中,方法的 签名 必须是唯一的。 方法签名包含方法名称、类型参数数量及其参数的数量、修饰 符和类型。 方法签名不包含返回类型。 当方法主体是单个表达式时,可使用紧凑表达式格式定义方法,如下例中所示: 参数用于将值或变量引用传递给方法。 方法参数从调用方法时指定的 自变量 中获取其实际值。 有四类参数:值 参数、引用参数、输出参数和参数数组。 值参数用于传递输入自变量。 值参数对应于局部变量,从为其传递的自变量中获取初始值。 修改值形参不会影 响为其传递的实参。 可以指定默认值,从而省略相应的自变量,这样值参数就是可选的。 引用参数用于按引用传递自变量。 为引用参数传递的自变量必须是一个带有明确值的变量。 在方法执行期间, 引用参数指出的存储位置与自变量相同。 引用参数使用 ref 修饰符进行声明。 下面的示例展示了如何使用 ref 参数
static void Swap(ref int x, ref int y) y =tenp public static void SwapExample( int i =1,j=2; Swap(ref i, ref j) Console. WriteLine ($11( ");//21 输出参数用于按引用传递自变量。输出参数与引用参数类似,不同之处在于,不要求向调用方提供的自变量显式 赋值。输出参数使用out修饰符进行声明。下面的示例演示如何通过C#7中引入的语法使用out参数 static void Divide (int x, int y, out int result, out int remainder) result =x/ y remainder =x% y; public static void OutUsageo Divide(10, 3, out int res, out int rem); Console. WriteLine(s"fres[rem");// 参数数组允许向方法传递数量不定的自变量。参数数组使用 params修饰符进行声明。参数数组只能是方法的 后一个参数且参数数组的类型必须是一维数组类型。 System. Console类的wite和 writeLine法是参数 数组用法的典型示例。它们的声明方式如下。 public class Console public static void Write(string fmt, params object[] args)t y public static void WriteLine (string fmt, params object[ args)t y 在使用参数数组的方法中,参数数组的行为与数组类型的常规参数完全相同。不过,在调用包含形参数组的方法 时,要么可以传递形参数组类型的一个实参,要么可以传递形参数组的元秦类型的任意数量实参。在后一种情况 中,数组实例会自动创建,并初始化为包含给定的自变量。以下示例 int x, y, z; Console. WriteLine (x=0 y=11 z=(2),x,y, z) 等同于编写以下代码
static void Swap(ref int x, ref int y) { int temp = x; x = y; y = temp; } public static void SwapExample() { int i = 1, j = 2; Swap(ref i, ref j); Console.WriteLine($"{i} {j}"); // "2 1" } static void Divide(int x, int y, out int result, out int remainder) { result = x / y; remainder = x % y; } public static void OutUsage() { Divide(10, 3, out int res, out int rem); Console.WriteLine($"{res} {rem}"); // "3 1" } public class Console { public static void Write(string fmt, params object[] args) { } public static void WriteLine(string fmt, params object[] args) { } // ... } int x, y, z; x = 3; y = 4; z = 5; Console.WriteLine("x={0} y={1} z={2}", x, y, z); 输出参数用于按引用传递自变量。 输出参数与引用参数类似,不同之处在于,不要求向调用方提供的自变量显式 赋值。 输出参数使用 out 修饰符进行声明。 下面的示例演示如何通过 C# 7 中引入的语法使用 out 参数。 参数数组 允许向方法传递数量不定的自变量。 参数数组使用 params 修饰符进行声明。 参数数组只能是方法的 最后一个参数,且参数数组的类型必须是一维数组类型。 System.Console 类的 Write 和 WriteLine 方法是参数 数组用法的典型示例。 它们的声明方式如下。 在使用参数数组的方法中,参数数组的行为与数组类型的常规参数完全相同。 不过,在调用包含形参数组的方法 时,要么可以传递形参数组类型的一个实参,要么可以传递形参数组的元素类型的任意数量实参。 在后一种情况 中,数组实例会自动创建,并初始化为包含给定的自变量。 以下示例: 等同于编写以下代码:
int x=3,y=4,z=5 string s=x=0) y=11 z=12" object[ args new object[3] args[0]=x args[1=y args[2]=z Console. WriteLine(s, args) 方法主体和局部变量 方法主体指定了在调用方法时执行的语句。 方法主体可以声明特定于方法调用的变量。此类变量称为局部变量。局部变量声明指定了类型名称、变量名称 以及可能的初始值。下面的示例声明了初始值为的局部变量1和无初始值的局部变量j。 class Squares public static void WriteSquareso while(i< 10) Console. WriteLine($"( x ( i=j1") C#要求必须先明确赋值局部变量,然后扌能获取其值。例如如果上述i的声明未包含初始值,那么编译器 会在后续使用1时报告错误,因为在后续使用时i不会在程序中得到明确赋值 方法可以使用 return语句将控制权返回给调用方。在返回void的方法中, return语句无法指定表达式。在 不返回void的方法中, return语句必须包括用于计算返回值的表达式。 静态和实例方法 使用 static修饰符声明的方法是静态方法。静态方法不对特定的实例起作用,只能直接访间静态成员。 未使用 static修饰符声明的方法是实例方法。实例方法对特定的实例起作用,并能够访问静态和实例成员。 中调用实例方法的实例可以作为this显式访问。在静态方法中引用this会生成错误。 以下 Entity类包含静态和实例成员
int x = 3, y = 4, z = 5; 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); 方法主体和局部变量 class Squares { public static void WriteSquares() { int i = 0; int j; while (i < 10) { j = i * i; Console.WriteLine($"{i} x {i} = {j}"); i = i + 1; } } } 静态和实例方法 方法主体指定了在调用方法时执行的语句。 方法主体可以声明特定于方法调用的变量。 此类变量称为 局部变量。 局部变量声明指定了类型名称、变量名称 以及可能的初始值。 下面的示例声明了初始值为零的局部变量 i 和无初始值的局部变量 j 。 C# 要求必须先 明确赋值 局部变量,然后才能获取其值。 例如,如果上述 i 的声明未包含初始值,那么编译器 会在后续使用 i 时报告错误,因为在后续使用时 i 不会在程序中得到明确赋值。 方法可以使用 return 语句将控制权返回给调用方。 在返回 void 的方法中, return 语句无法指定表达式。 在 不返回 void 的方法中, return 语句必须包括用于计算返回值的表达式。 使用 static 修饰符声明的方法是静态方法。 静态方法不对特定的实例起作用,只能直接访问静态成员。 未使用 static 修饰符声明的方法是实例方法。 实例方法对特定的实例起作用,并能够访问静态和实例成员。 其 中调用实例方法的实例可以作为 this 显式访问。 在静态方法中引用 this 会生成错误。 以下 Entity 类包含静态和实例成员
static int s nextseriaINo; public Entity( public int GetserialNoo return serialNo public static int GetNextseriaINoO) re public static void SetNextserialNo(int val s nextSerialNo value; 每个 Entity实例均有一个序列号(很可能包含此处未显示的其他-些信息)。 Entity构造函数(类似于实例方 法)将新实例初始化为包含下一个可用的序列号。由于构造函数是实例成员,因此可以访问51a1o实例字段 和5 nextSerialNo静态字段。 et8和 SetNextserialN静态方法可以访间5se静态字段但如果直接访间 rialtO 实例字段,则会生成错误 下例显示了 Entity类的用法。 Entity. SetNextSerialNo(1000) Entity el new Entity () Entity e2 new Entity ( Console. WriteLine(el. Get SerialNoO) / Outputs 1000 Console. WriteLine(e2. GetSerialNoo)) / Outputs "1001 Console. WriteLine(Entity. GetNextSerialNo O); / Outputs "1002 SetNextserialNo和 GetNextserialNo静态方法在类中进行调用,而 etserialNo实例方法则是在类实例中进行 调用。 虚方法、重写方法和抽象方法 如果实例方法声明中有 virtual修饰符,可以捋实例方法称为虚方法。如果没有 virtual修饰符,可以将实例 方法称为非虚方法 调用虚方法时,为其调用方法的实例的运行时类型决定了要调用的实际方法实现代码。调用非虚方法时,实例 的编译时类型是决定性因秦。 可以在派生类中重军虚方法。如果实例方法声明中有 override修饰符,那么实例方法可以重写签名相同的继承 虚方法。虚方法声明引入了新方法。重写方法声明通过提供现有继承的虚方法的新实现,专门针对该方法。 抽象方法是没有实现代码的虚方法。抽象方法使用 abstract修饰符进行声明,仅可在抽象类中使用。必须在 有非抽象派生类中重写抽象方法 下面的示例声明了一个抽象类 Expression,用于表示表达式树节点;还声明了三个派生类( Constant VariableReference和 Operation),用于实现常量、变量引用和算术运算的表达式树节点。(该示例与表达式树
class Entity { static int s_nextSerialNo; int _serialNo; public Entity() { _serialNo = s_nextSerialNo++; } public int GetSerialNo() { return _serialNo; } public static int GetNextSerialNo() { return s_nextSerialNo; } public static void SetNextSerialNo(int value) { s_nextSerialNo = value; } } 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" 虚方法、重写方法和抽象方法 每个 Entity 实例均有一个序列号(很可能包含此处未显示的其他一些信息)。 Entity 构造函数(类似于实例方 法)将新实例初始化为包含下一个可用的序列号。 由于构造函数是实例成员,因此可以访问 _serialNo 实例字段 和 s_nextSerialNo 静态字段。 GetNextSerialNo 和 SetNextSerialNo 静态方法可以访问 s_nextSerialNo 静态字段,但如果直接访问 _serialNo 实例字段,则会生成错误。 下例显示了 Entity 类的用法。 SetNextSerialNo 和 GetNextSerialNo 静态方法在类中进行调用,而 GetSerialNo 实例方法则是在类实例中进行 调用。 如果实例方法声明中有 virtual 修饰符,可以将实例方法称为“虚方法”。 如果没有 virtual 修饰符,可以将实例 方法称为“非虚方法”。 调用虚方法时,为其调用方法的实例的 运行时类型 决定了要调用的实际方法实现代码。 调用非虚方法时,实例 的 编译时类型 是决定性因素。 可以在派生类中 重写 虚方法。 如果实例方法声明中有 override 修饰符,那么实例方法可以重写签名相同的继承 虚方法。 虚方法声明引入了新方法。 重写方法声明通过提供现有继承的虚方法的新实现,专门针对该方法。 抽象方法 是没有实现代码的虚方法。 抽象方法使用 abstract 修饰符进行声明,仅可在抽象类中使用。 必须在 所有非抽象派生类中重写抽象方法。 下面的示例声明了一个抽象类 Expression ,用于表示表达式树节点;还声明了三个派生类( Constant 、 VariableReference 和 Operation ),用于实现常量、变量引用和算术运算的表达式树节点。 (该示例与表达式树