🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# C# 中的方法 > 原文: [https://zetcode.com/lang/csharp/methods/](https://zetcode.com/lang/csharp/methods/) 在本教程的这一部分中,我们将介绍 C# 方法。 在面向对象的编程中,我们使用对象。 对象是程序的基本构建块。 对象由数据和方法组成。 方法更改创建的对象的状态。 它们是对象的动态部分。 数据是静态部分。 ## C# 方法定义 方法是包含一系列语句的代码块。 方法必须在类或结构中声明。 好的编程习惯是方法仅执行一项特定任务。 方法为程序带来了模块化。 正确使用方法具有以下优点: * 减少代码重复 * 将复杂的问题分解成更简单的部分 * 提高代码的清晰度 * 重用代码 * 信息隐藏 ## C# 方法特征 方法的基本特征是: * 访问权限 * 返回值类型 * 方法名称 * 方法参数 * 括号 * 语句块 方法的访问级别由访问修饰符控制。 他们设置方法的可见性。 他们确定谁可以调用该方法。 方法可以将值返回给调用方。 如果我们的方法返回一个值,我们将提供其数据类型。 如果不是,则使用`void`关键字指示我们的方法不返回值。 方法参数用括号括起来,并用逗号分隔。 空括号表示该方法不需要任何参数。 方法块周围包含{}个字符。 当方法被调用时,该块包含一个或多个执行的语句。 拥有一个空的方法块是合法的。 ## C# 方法签名 方法签名是 C# 编译器方法的唯一标识。 签名由方法名称以及其每个形式参数的类型和种类(值,引用或输出)组成。 方法签名不包括返回类型。 可以在方法名称中使用任何合法字符。 按照约定,方法名称以大写字母开头。 方法名称是动词或动词,后跟形容词或名词。 随后的每个单词都以大写字母开头。 以下是 C# 中方法的典型名称: * `Execute` * `FindId` * `SetName` * `GetName` * `CheckIfValid` * `TestValidity` ## C# 简单示例 我们从一个简单的例子开始。 `Program.cs` ```cs using System; namespace SimpleMethod { class Base { public void ShowInfo() { Console.WriteLine("This is Base class"); } } class Program { static void Main(string[] args) { Base bs = new Base(); bs.ShowInfo(); } } } ``` 我们有一个`ShowInfo()`方法,它打印其类的名称。 ```cs class Base { public void ShowInfo() { Console.WriteLine("This is Base class"); } } ``` 每个方法都必须在类或结构内定义。 它必须有一个名字。 在我们的情况下,名称为`ShowInfo()`。 方法名称之前的关键字是访问说明符和返回类型。 括号跟随方法的名称。 它们可能包含方法的参数。 我们的方法没有任何参数。 ```cs static void Main() { ... } ``` 这是`Main()`方法。 它是每个控制台或 GUI 应用的入口点。 必须声明为`static`。 我们将在后面看到原因。 `Main()`方法的返回类型可以是`void`或`int`。 省略了`Main()`方法的访问说明符。 在这种情况下,将使用默认值`private`。 不建议对`Main()`方法使用`public`访问说明符。 程序集中的任何其他方法都不应调用它。 当应用启动时,只有 CLR 才能调用它。 ```cs Base bs = new Base(); bs.ShowInfo(); ``` 我们创建`Base`类的实例。 我们在对象上调用`ShowInfo()`方法。 我们说该方法是一个实例方法,因为它需要一个实例来调用。 通过指定对象实例,成员访问运算符(点),方法名称,来调用该方法。 ## C# 方法参数 参数是传递给方法的值。 方法可以采用一个或多个参数。 如果方法使用数据,则必须将数据传递给方法。 我们通过在括号内指定它们来实现。 在方法定义中,我们必须为每个参数提供名称和类型。 `Program.cs` ```cs using System; namespace MethodParameters { class Addition { public int AddTwoValues(int x, int y) { return x + y; } public int AddThreeValues(int x, int y, int z) { return x + y + z; } } class Program { static void Main(string[] args) { Addition a = new Addition(); int x = a.AddTwoValues(12, 13); int y = a.AddThreeValues(12, 13, 14); Console.WriteLine(x); Console.WriteLine(y); } } } ``` 在上面的示例中,我们有两种方法。 其中一个带有两个参数,另一个带有三个参数。 ```cs public int AddTwoValues(int x, int y) { return x + y; } ``` `AddTwoValues()`方法采用两个参数。 这些参数具有`int`类型。 该方法还向调用者返回一个整数。 我们使用`return`关键字从方法中返回一个值。 ```cs public int AddThreeValues(int x, int y, int z) { return x + y + z; } ``` `AddThreeValues()`与先前的方法相似。 它带有三个参数。 ```cs int x = a.AddTwoValues(12, 13); ``` 我们称为加法对象的`AddTwoValues()`方法。 它有两个值。 这些值将传递给方法。 该方法返回一个分配给`x`变量的值。 ## C# 变量数量可变 方法可以采用可变数量的参数。 为此,我们使用`params`关键字。 `params`关键字之后不允许有其他参数。 方法声明中仅允许使用一个`params`关键字。 `Program.cs` ```cs using System; namespace SumOfValues { class Program { static void Main(string[] args) { Sum(1, 2, 3); Sum(1, 2, 3, 4, 5); } static void Sum(params int[] list) { Console.WriteLine("There are {0} items", list.Length); int sum = 0; foreach (int i in list) { sum = sum + i; } Console.WriteLine("Their sum is {0}", sum); } } } ``` 我们创建一个`Sum()`方法,该方法可以使用可变数量的参数。 该方法将计算传递给该方法的值的总和。 ```cs Sum(1, 2, 3); Sum(1, 2, 3, 4, 5); ``` 我们两次调用`Sum()`方法。 在一种情况下,它需要 3 个参数,在第二种情况下,它需要 5 个参数。我们调用相同的方法。 ```cs static void Sum(params int[] list) { ... } ``` `Sum()`方法可以采用可变数量的整数值。 所有值都添加到列表数组中。 ```cs Console.WriteLine("There are {0} items", list.Length); ``` 我们打印列表数组的长度。 ```cs int sum = 0; foreach (int i in list) { sum = sum + i; } ``` 我们计算列表中值的总和。 ```cs $ dotnet run There are 3 items Their sum is 6 There are 5 items Their sum is 15 ``` 这是示例的输出。 ## C# 返回元组 C# 方法可以使用元组返回多个值。 `Program.cs` ```cs using System; using System.Collections.Generic; using System.Linq; namespace ReturingTuples { class Program { static void Main(string[] args) { var vals = new List<int> { 11, 21, 3, -4, -15, 16, 5 }; (int min, int max, int sum) = BasicStats(vals); Console.WriteLine($"Minimum: {min}, Maximum: {max}, Sum: {sum}"); } static (int, int, int) BasicStats(List<int> vals) { int sum = vals.Sum(); int min = vals.Min(); int max = vals.Max(); return (min, max, sum); } } } ``` 我们有`BasicStats()`方法,该方法返回整数列表的基本统计信息。 ```cs using System.Linq; ``` 我们需要为`Sum()`,`Min()`和`Max()`扩展方法导入`System.Linq`。 ```cs var vals = new List<int> { 11, 21, 3, -4, -15, 16, 5 }; ``` 我们有一个整数值列表。 我们要根据这些值计算一些基本统计数据。 ```cs (int min, int max, int sum) = BasicStats(vals); ``` 我们使用解构操作将元组元素分配给三个变量。 ```cs static (int, int, int) BasicStats(List<int> vals) { ``` 方法声明指定我们返回一个元组。 ```cs return (min, max, sum); ``` 我们返回三个元素的元组。 ```cs $ dotnet run Minimum: -15, Maximum: 21, Sum: 37 ``` 这是输出。 ## C# 匿名方法 匿名方法是没有名称的内联方法。 匿名方法消除了创建单独方法的需要,从而减少了编码开销。 如果没有匿名方法,开发者通常不得不创建一个类来仅调用一个方法。 `Program.cs` ```cs using System; using System.Timers; namespace AnonymousMethod { class Program { static void Main(string[] args) { var timer = new Timer(); timer.Elapsed += new ElapsedEventHandler( delegate(object source, ElapsedEventArgs e) { Console.WriteLine("Event triggered at {0}", e.SignalTime); } ); timer.Interval = 2000; timer.Enabled = true; Console.ReadLine(); } } } ``` 我们创建一个计时器对象,然后每 2 秒调用一个匿名方法。 ```cs var timer = new Timer(); ``` `Timer`类在应用中生成重复事件。 ```cs timer.Elapsed += new ElapsedEventHandler( delegate(object source, ElapsedEventArgs e) { Console.WriteLine("Event triggered at {0}", e.SignalTime); } ); ``` 在这里,我们将匿名方法插入`Elapsed`事件。 `delegate`关键字用于表示匿名方法。 ```cs Console.ReadLine(); ``` 此时,程序等待来自用户的输入。 当我们按下`Return`键时,程序结束。 否则,程序将在事件生成之前立即完成。 ## C# 通过值,通过引用传递参数 C# 支持两种将参数传递给方法的方式:按值和按引用。 参数的默认传递是按值传递。 当我们按值传递参数时,该方法仅适用于值的副本。 当我们处理大量数据时,这可能会导致性能开销。 我们使用`ref`关键字通过引用传递值。 当我们通过引用传递值时,该方法会收到对实际值的引用。 修改后,原始值会受到影响。 这种传递值的方式更加节省时间和空间。 另一方面,它更容易出错。 我们应该使用哪种方式传递参数? 这取决于实际情况。 假设我们有一组数据,例如员工工资。 如果我们要计算数据的某些统计信息,则无需修改它们。 我们可以传递值。 如果我们处理大量数据,并且计算速度至关重要,则可以引用。 如果我们要修改数据,例如进行一些减薪或加薪,我们可以引用一下。 以下示例显示了如何通过值传递参数。 `Program.cs` ```cs using System; namespace PassingByValues { class Program { static int a = 4; static int b = 7; static void Main(string[] args) { Console.WriteLine("Outside Swap method"); Console.WriteLine("a is {0}", a); Console.WriteLine("b is {0}", b); Swap(a, b); Console.WriteLine("Outside Swap method"); Console.WriteLine("a is {0}", a); Console.WriteLine("b is {0}", b); } static void Swap(int a, int b) { int temp = a; a = b; b = temp; Console.WriteLine("Inside Swap method"); Console.WriteLine("a is {0}", a); Console.WriteLine("b is {0}", b); } } } ``` `Swap()`方法在`a`和`b`变量之间交换数字。 原始变量不受影响。 ```cs static int a = 4; static int b = 7; ``` 最初,这两个变量被启动。 变量必须声明为`static`,因为它们是从静态方法中使用的。 ```cs Swap(a, b); ``` 我们称为`Swap()`方法。 该方法将`a`和`b`变量作为参数。 ```cs int temp = a; a = b; b = temp; ``` 在`Swap()`方法内部,我们更改了值。 请注意,`a`和`b`变量是在本地定义的。 它们仅在`Swap()`方法内部有效。 ```cs $ dotnet run Outside Swap method a is 4 b is 7 Inside Swap method a is 7 b is 4 Outside Swap method a is 4 b is 7 ``` 输出显示原始变量不受影响。 下一个代码示例通过引用将值传递给方法。 原始变量在`Swap()`方法内更改。 方法定义和方法调用都必须使用`ref`关键字。 `Program.cs` ```cs using System; namespace PassingByReference { class Program { static int a = 4; static int b = 7; static void Main(string[] args) { Console.WriteLine("Outside Swap method"); Console.WriteLine("a is {0}", a); Console.WriteLine("b is {0}", b); Swap(ref a, ref b); Console.WriteLine("Outside Swap method"); Console.WriteLine("a is {0}", a); Console.WriteLine("b is {0}", b); } static void Swap(ref int a, ref int b) { int temp = a; a = b; b = temp; Console.WriteLine("Inside Swap method"); Console.WriteLine("a is {0}", a); Console.WriteLine("b is {0}", b); } } } ``` 在此示例中,调用`Swap()`方法将更改原始值。 ```cs Swap(ref a, ref b); ``` 我们用两个参数调用该方法。 它们前面带有`ref`关键字,指示我们正在通过引用传递参数。 ```cs static void Swap(ref int a, ref int b) { ... } ``` 同样在方法声明中,我们使用`ref`关键字来通知编译器我们接受对参数而不是值的引用。 ```cs $ dotnet run Outside Swap method a is 4 b is 7 Inside Swap method a is 7 b is 4 Outside Swap method a is 7 b is 4 ``` 在这里,我们看到`Swap()`方法确实改变了变量的值。 `out`关键字类似于`ref`关键字。 不同之处在于,使用`ref`关键字时,必须在传递变量之前对其进行初始化。 使用`out`关键字,可能无法初始化。 方法定义和方法调用都必须使用`out`关键字。 `Program.cs` ```cs using System; namespace OutKeyword { class Program { static void Main(string[] args) { int val; SetValue(out val); Console.WriteLine(val); } static void SetValue(out int i) { i = 12; } } } ``` 一个示例显示`out`关键字的用法。 ```cs int val; SetValue(out val); ``` 声明了`val`变量,但未初始化。 我们将变量传递给`SetValue()`方法。 ```cs static void SetValue(out int i) { i = 12; } ``` 在`SetValue()`方法内部,分配了一个值,该值随后会打印到控制台。 ## C# 方法重载 方法重载允许创建多个具有相同名称的方法,它们的输入类型彼此不同。 方法重载有什么好处? Qt5 库提供了一个很好的用法示例。 `QPainter`类具有三种绘制矩形的方法。 它们的名称为`drawRect()`,其参数不同。 一个引用一个浮点矩形对象,另一个引用一个整数矩形对象,最后一个引用四个参数:`x`,`y`,`width`,`height`。 如果开发 Qt 的 C++ 语言没有方法重载,则库的创建者必须将其命名为`drawRectRectF()`,`drawRectRect()`和`drawRectXYWH()`之类的方法。 方法重载的解决方案更为优雅。 `Program.cs` ```cs using System; namespace Overloading { class Sum { public int GetSum() { return 0; } public int GetSum(int x) { return x; } public int GetSum(int x, int y) { return x + y; } } class Program { static void Main() { var s = new Sum(); Console.WriteLine(s.GetSum()); Console.WriteLine(s.GetSum(20)); Console.WriteLine(s.GetSum(20, 30)); } } } ``` 我们有三种方法`GetSum()`。 它们的输入参数不同。 ```cs public int GetSum(int x) { return x; } ``` 这一个参数。 ```cs Console.WriteLine(s.GetSum()); Console.WriteLine(s.GetSum(20)); Console.WriteLine(s.GetSum(20, 30)); ``` 我们调用这三种方法。 ```cs $ dotnet run 0 20 50 ``` 这就是我们运行示例时得到的。 ## C# 递归 在数学和计算机科学中,递归是一种定义方法的方法,其中所定义的方法在其自己的定义内应用。 换句话说,递归方法会调用自身来完成其工作。 递归是解决许多编程任务的一种广泛使用的方法。 一个典型的例子是阶乘的计算。 `Program.cs` ```cs using System; namespace Recursion { class Program { static void Main(string[] args) { Console.WriteLine(Factorial(6)); Console.WriteLine(Factorial(10)); } static int Factorial(int n) { if (n == 0) { return 1; } else { return n * Factorial(n-1); } } } } ``` 在此代码示例中,我们计算两个数字的阶乘。 ```cs return n * Factorial(n-1); ``` 在阶乘方法的主体内部,我们将阶乘方法称为经过修改的参数。 该函数调用自身。 ```cs $ dotnet run 720 3628800 ``` 这些就是结果。 ## C# 方法作用域 在方法内部声明的变量具有方法作用域。 名称的作用域是程序文本的区域,在该区域内,可以引用由名称声明的实体而无需使用名称限定。 在方法内部声明的变量具有方法作用域。 它也称为本地作用域。 该变量仅在此特定方法中有效。 `Program.cs` ```cs using System; namespace MethodScope { class Test { int x = 1; public void exec1() { Console.WriteLine(this.x); Console.WriteLine(x); } public void exec2() { int z = 5; Console.WriteLine(x); Console.WriteLine(z); } } class Program { static void Main(string[] args) { var ts = new Test(); ts.exec1(); ts.exec2(); } } } ``` 在前面的示例中,我们在`exec1()`和`exec2()`方法之外定义了`x`变量。 该变量具有类作用域。 它在`Test`类的定义内的任何地方都有效,例如大括号之间。 ```cs public void exec1() { Console.WriteLine(this.x); Console.WriteLine(x); } ``` `x`变量(也称为`x`字段)是一个实例变量。 因此,可以通过`this`关键字进行访问。 它在`exec1()`方法中也有效,并且可以用其裸名引用。 这两个语句都引用相同的变量。 ```cs public void exec2() { int z = 5; Console.WriteLine(x); Console.WriteLine(z); } ``` 也可以在`exec2()`方法中访问 x 变量。 `z`变量在`exec2()`方法中定义。 它具有方法作用域。 仅在此方法中有效。 ```cs $ dotnet run 1 1 1 5 ``` 这是程序的输出。 在方法内部定义的变量具有本地/方法作用域。 如果局部变量与实例变量具有相同的名称,则它会遮盖实例变量。 通过使用`this`关键字,类变量仍可在方法内部访问。 `Program.cs` ```cs using System; namespace Shadowing { class Test { int x = 1; public void exec() { int x = 3; Console.WriteLine(this.x); Console.WriteLine(x); } } class Program { static void Main(string[] args) { var ts = new Test(); ts.exec(); } } } ``` 在前面的示例中,我们在`exec()`方法外部和`exec()`方法内部声明`x`变量。 这两个变量具有相同的名称,但是它们没有冲突,因为它们存在于不同的作用域内。 ```cs Console.WriteLine(this.x); Console.WriteLine(x); ``` 变量的访问方式不同。 方法内定义的`x`变量(也称为局部变量)仅通过其名称即可访问。 可以使用`this`关键字引用实例变量。 ```cs $ dotnet run 1 3 ``` This is the output of the program. ## C# 静态方法 在没有对象实例的情况下调用静态方法。 要调用静态方法,我们使用类的名称和点运算符。 静态方法只能与静态成员变量一起使用。 静态方法通常用于表示不会随对象状态变化的数据或计算。 数学库是一个示例,其中包含用于各种计算的静态方法。 我们使用`static`关键字声明一个静态方法。 如果不存在静态修饰符,则该方法称为实例方法。 我们不能在静态方法中使用`this`关键字。 它只能在实例方法中使用。 `Main()`方法是 C# 控制台和 GUI 应用的入口点。 在 C# 中,要求`Main()`方法是静态的。 在应用启动之前,尚未创建任何对象。 要调用非静态方法,我们需要有一个对象实例。 静态方法在实例化类之前就已存在,因此将静态方法应用于主入口点。 `Program.cs` ```cs using System; namespace StaticMethod { class Basic { static int Id = 2321; public static void ShowInfo() { Console.WriteLine("This is Basic class"); Console.WriteLine("The Id is: {0}", Id); } } class Program { static void Main(string[] args) { Basic.ShowInfo(); } } } ``` 在我们的代码示例中,我们定义了静态`ShowInfo()`方法。 ```cs static int Id = 2321; ``` 静态方法只能使用静态变量。 ```cs public static void ShowInfo() { Console.WriteLine("This is Basic class"); Console.WriteLine("The Id is: {0}", Id); } ``` 这是我们的静态`ShowInfo()`方法。 它与静态 ID 成员一起使用。 ```cs Basic.ShowInfo(); ``` 要调用静态方法,我们不需要对象实例。 我们通过使用类的名称和点运算符来调用该方法。 ```cs $ dotnet run This is Basic class The Id is: 2321 ``` This is the output of the example. ## C# 隐藏方法 当派生类从基类继承时,它可以定义基类中已经存在的方法。 我们说隐藏了我们从中派生的类的方法。 为了明确告知编译器我们打算隐藏方法的意图,我们使用`new`关键字。 没有此关键字,编译器将发出警告。 `Program.cs` ```cs using System; namespace HidingMethods { class Base { public void Info() { Console.WriteLine("This is Base class"); } } class Derived : Base { public new void Info() { base.Info(); Console.WriteLine("This is Derived class"); } } class Program { static void Main(string[] args) { var d = new Derived(); d.Info(); } } } ``` 我们有两个类:`Derived`和`Base`类。 `Derived`类继承自`Base`类。 两者都有一种称为`Info()`的方法。 ```cs class Derived : Base { ... } ``` (:)字符用于从类继承。 ```cs public new void Info() { base.Info(); Console.WriteLine("This is Derived class"); } ``` 这是`Derived`类中`Info()`方法的实现。 我们使用`new`关键字通知编译器我们正在从基类中隐藏方法。 请注意,我们仍然可以达到原始的`Info()`方法。 借助`base`关键字,我们也调用了`Base`类的`Info()`方法。 ```cs $ dotnet run This is Base class This is Derived class ``` 我们已经调用了这两种方法。 ## C# 覆盖方法 现在,我们将引入两个新的关键字:`virtual`关键字和`override`关键字。 它们都是方法修饰符。 它们用于实现对象的多态行为。 `virtual`关键字创建一个虚拟方法。 可以在派生类中重新定义虚拟方法。 稍后在派生类中,我们使用`override`关键字重新定义相关方法。 如果派生类中的方法前面带有`override`关键字,则派生类的对象将调用该方法,而不是基类方法。 `Program.cs` ```cs using System; namespace Overriding { class Base { public virtual void Info() { Console.WriteLine("This is Base class"); } } class Derived : Base { public override void Info() { Console.WriteLine("This is Derived class"); } } class Program { static void Main(string[] args) { Base[] objs = { new Base(), new Derived(), new Base(), new Base(), new Base(), new Derived() }; foreach (Base obj in objs) { obj.Info(); } } } } ``` 我们创建`Base`和`Derived`对象的数组。 我们遍历数组并在所有数组上调用`Info()`方法。 ```cs public virtual void Info() { Console.WriteLine("This is Base class"); } ``` 这是`Base`类的虚拟方法。 期望在派生类中重写它。 ```cs public override void Info() { Console.WriteLine("This is Derived class"); } ``` 我们将覆盖`Derived`类中的基本`Info()`方法。 我们使用`override`关键字。 ```cs Base[] objs = { new Base(), new Derived(), new Base(), new Base(), new Base(), new Derived() }; ``` 在这里,我们创建`Base`和`Derived`对象的数组。 请注意,我们在数组声明中使用了`Base`类型。 这是因为`Derived`类可以继承,因此可以转换为`Base`类。 相反的说法是不正确的。 将两个对象放在一个数组中的唯一方法是对所有可能的对象使用在继承层次结构中最顶层的类型。 ```cs foreach (Base obj in objs) { obj.Info(); } ``` 我们遍历数组,并在数组中的所有对象上调用`Info()`。 ```cs $ dotnet run This is Base class This is Derived class This is Base class This is Base class This is Base class This is Derived class ``` 这是输出。 现在,将`new`关键字更改为`override`关键字。 再次编译该示例并运行它。 ```cs $ dotnet run This is Base class This is Base class This is Base class This is Base class This is Base class This is Base class ``` 这次我们有不同的输出。 ## C# 本地函数 C# 7.0 引入了本地功能。 这些是在其他方法中定义的函数。 `Program.cs` ```cs using System; namespace LocalFunction { class Program { static void Main(string[] args) { Console.Write("Enter your name: "); string name = Console.ReadLine(); string message = BuildMessage(name); Console.WriteLine(message); string BuildMessage(string value) { string msg = String.Format("Hello {0}!", value); return msg; } } } } ``` 在示例中,我们有一个局部函数`BuildMessage()`,它在`Main()`方法内部定义和调用。 ## C# 密封方法 密封方法将覆盖具有相同签名的继承虚拟方法。 密封方法也应标有倍率修饰符。 使用`sealed`修饰符可防止派生类进一步覆盖该方法。 和这两个字很重要。 首先,方法必须是虚拟的。 必须稍后将其覆盖。 至此,可以将其密封。 `Program.cs` ```cs using System; namespace SealedMethods { class A { public virtual void F() { Console.WriteLine("A.F"); } public virtual void G() { Console.WriteLine("A.G"); } } class B : A { public override void F() { Console.WriteLine("B.F"); } public sealed override void G() { Console.WriteLine("B.G"); } } class C : B { public override void F() { Console.WriteLine("C.F"); } /*public override void G() { Console.WriteLine("C.G"); }*/ } class SealedMethods { static void Main(string[] args) { B b = new B(); b.F(); b.G(); C c = new C(); c.F(); c.G(); } } } ``` 在前面的示例中,我们密封了类`B`中的方法`G()`。 ```cs public sealed override void G() { Console.WriteLine("B.G"); } ``` 方法`G()`会覆盖`B`类的祖先中具有相同名称的方法。 它也被密封以防止进一步取代该方法。 ```cs /*public override void G() { Console.WriteLine("C.G"); }*/ ``` 这些行被注释,因为否则代码示例将无法编译。 编译器将给出以下错误:`Program.cs`(38,30):错误 CS0239:`C.G()`:无法覆盖继承的成员`B.G()`,因为它是密封的 ```cs c.G(); ``` 此行将`"B.G()"`打印到控制台。 ```cs $ dotnet run B.F B.G C.F B.G ``` This is the output. ## C# 方法的表达式主体定义 方法的表达主体定义使我们能够以非常简洁,易读的形式定义方法实现。 ```cs method declaration => expression ``` 这是一般语法。 `Program.cs` ```cs using System; namespace ExpBodyDef { class User { public string Name { get; set; } public string Occupation { get; set; } public override string ToString() => $"{Name} is a {Occupation}"; } class Program { static void Main (string[] args) { var user = new User(); user.Name = "John Doe"; user.Occupation = "gardener"; Console.WriteLine(user); } } } ``` 在示例中,我们为`ToString()`方法的主体提供了一个表达式主体定义。 ```cs public override string ToString() => $"{Name} is a {Occupation}"; ``` 表达式主体定义简化了语法。 在 C# 教程的这一部分中,我们介绍了方法。