目录
方法
1.方法的定义
方法是一块有名称的代码。(方法是类的函数成员)
我们可以使用方法的名称从别的地方执行这块代码,也可以把数据输入方法并接受数据输出。
方法由两部分组成:方法头和方法体
方法头的具体结构:
方法体的具体结构:
注意:
与C和C++不同:
- C#中没有 全局函数(也就是方法) 声明在类型声明的外部。
- C#中方法也没有默认的返回类型,所有方法必须包含返回类型或void。
2.方法组成详解
我们会先从方法体的局部变量讲起,再讲程序控制流结构和方法调用;然后讲方法体的返回类型和参数列表;最后讲方法重载。除此之外,我们还会简单讲一下栈帧和递归的含义。
2.1 局部变量
局部变量也保存数据。经常用于保存局部的或临时的计算数据。
-
局部变量的生存周期仅限于创建它的块以及它的块包含的嵌套块。
-
可以在方法块的任意位置声明局部变量,但必须在使用它们前声明。
字段是在类或结构中直接声明的任意类型的变量。 字段是其包含类型的成员。
2.1.1 var关键字(类型推断)
我们在声明局部变量时,通常的写法如下:
class 第十章方法
{
static void Main()
{
int a=15;
第十章方法 meth=new 第十章方法();
}
}
我们可以这样理解,var关键字的作用是使编译器能从初始化语句的右边推断出数据的类型。
var的意义在于当类型的名称过于长时,我们可以像下面写法来简化代码:
class 第十章方法
{
static void Main()
{
var a=15;
var meth=new 第十章方法();
}
}
使用var关键字的条件:
-
只能用于局部变量,不能用于字段。(也就是说不能用于类或结构中直接声明的变量)
-
只能在变量声明中包含初始化时使用。
-
一旦编译器推断出变量的类型,它就是固定且不可更改的。
注意:var关键字不像javascript那样可以引用不同的类型。不改变C#的强类型性质。
2.1.2 嵌套块中的局部变量
我们可以从下图了解嵌套块中的局部变量以及局部变量的生存周期。
注意:在C和C++中,可以先声明一个局部变量,再在其嵌套块中再声明一个名称相同的局部变量。(在嵌套块范围内,嵌套块内的局部变量掩盖嵌套块外的局部变量) 然而对于C#,无论怎样,都不允许第一个局部变量生成周期内,含有另一个同名的局部变量。
2.2 局部常量(常变量)
局部常量与局部变量类似,只是一旦被初始化,它的值就不能被改变。
使用局部常量的两个条件:
- 在声明时必须初始化
- 在声明后不能改变
局部常量语法:const 数据类型 变量名=值;
const为关键字,不是修饰符,必须放在最前面
2.3 程序控制流结构
详见第九章中1.程序控制流结构。
2.4 方法调用
我们可以从方法体内部调用其他方法。
我们可以发现,想要在一个方法块内调用另一个不在本方法块内的方法,我们需要实例化一个类,再从实例成员调用方法。(那些嵌套在方法内的方法叫局部函数)
方法块内的代码可以直接调用局部函数。

2.5 返回值
方法可以向调用它的代码返回一个值。
方法可以选择返回一个值,也可以不返回值。
方法要返回一个值的时候要在方法块内添加一句 return 返回值(变量名或者对象名);
2.5.1 返回语句和void方法
方法会执行到return时结束,所以即使没有返回值,但运行到一定程度想要结束方法时,可以在想要结束的位置后面添一句reutrn;就可以了,但要注意,有返回值时要记得在后面添加返回值哦
3 参数的六种类型
参数允许你实现两件事。一,在方法开始执行的时候把数据传入方法。二,返回多个值给调用代码。
参数有六种类型,各自以略微不同的方式从方法出入或传出数据。
- 值参数
- 引用参数
- 输出参数
- 参数数组
- 命名参数
- 可选参数
方法中的参数又可以分为形参和实参。
形参(局部变量)
- 形参与方法快内的其他局部变量不同,参数在方法体的外面定义并初始化。(参数在方法体的外面定义并在方法开始之前初始化)
- 可以有任意数目的形参声明,需要用逗号隔开
实参
当代码调用一个方法时,参数在方法体的外面定义并在方法开始之前初始化。
用于初始化形参的表达式和变量称为实参。
3.1 值参数
上图看到的这种类型是默认的类型,称为值参数。
值参数的定义:把实参的值复制给形参。
当方法被调用时,系统执行以下操作:
- 在栈中为形参分配空间
- 将实参的值复制给形参
值参数的实参不一定是变量,它可以是任何能计算成相应数据类型的表达式。
注意:
- 用作实参之前,变量必须被赋值。
- 对于引用类型,变量可以设置为null或者实际的引用。
- 值参数和值类型不是同一个东西。值参数是把实参的值复制给形参、值类型是值类型本身包含其值。
示例代码:
方法的调用过程:
我们可以发现,值参数中值类型在方法结束后不会改变,而引用类型则会发生改变。
3.2 引用参数
引用参数的定义:
- 使用引用参数时,必须在方法的声明和调用中使用ref修饰符。
- 实参必须是变量,在用作实参前必须赋值。对于引用类型变量可以设置为null或者实际的引用。
- 不会在栈中为形参分配空间。
- 形参的参数名将作为实参变量的别名,指向同一内存位置。因此在方法执行中发生对形参的改变,在方法结束后也不会消失。
示例:
当方法被调用时,系统执行以下操作:
- 不会在栈中为形参分配空间
- 形参的参数名将作为实参变量的别名,指向同一内存位置
3.2.1 ref局部变量和ref返回
ref除了可以用作引用参数的关键字。还有两种额外的功能。
- 一个是ref局部变量,它允许一个变量是另一个变量的别名。
- 一个是ref返回功能,它允许你将一个引用发送到方法外,然后在调用上下文内使用这个引用。
ref局部变量
ref局部变量的定义:
- ref可以使一个变量是另一个变量的别名(值类型也可以)
- 使用ref创建的别名被影响都会反映到另一个变量上,因为它们引用的是相同的对象(值类型也一样)
语法: ref 数据类型 变量=ref 别名
示例:ref int a= ref y;
ref返回
ref局部变量通常与ref返回一起使用。
ref返回功能提供一种使方法返回变量引用而不是变量值的方法。
示例:
结果返回:
Value inside class object:5
Value inside class object:10
ref返回的限制:
- 不能将返回类型是void的方法声明为ref返回方法
- return ref 返回值 表达式不能返回如下内容:
- 空值
- 常量
- 枚举成员
- 类或者结构体的属性
- 指向只读位置的指针
- return ref 返回值 表达式只能指向原先就在调用域内的位置、或字段。所以,它不能指向方法的局部变量
- ref局部变量只能被赋值一次。
- 即使将一个方法声明为ref返回方法,但要是调用方法的时候省略了ref关键字,则返回的是值,不是指向值内存的指针。
- 如果ref局部变量作为常规的实际参数传递给其他方法,则该方法仅获得该变量的值。
3.3 引用类型作为值参数和引用参数
这里我们主要讨论 在方法块内部设置形参会发生声明。
- 将引用类型对象作为值参数传递:
- 如果在方体块内创建一个新的对象赋值给形参,将切断形参和实参的连系。在方法调用结束后,新对象将不复存在。
- 示例图
1.19 示例 -
1.20调用过程图 - 将引用类型对象作为引参数传递:
- 如果在方体块内创建一个新的对象赋值给形参,在方法调用结束后,新对象依然存在,并且是实参所引用的值
1.21示例图 -
1.22调用过程图
- 如果在方体块内创建一个新的对象赋值给形参,在方法调用结束后,新对象依然存在,并且是实参所引用的值
3.4 输出参数
输出参数用于从方法体内把数据传出到调用代码,它们的行为与引用参数类似。
使用输出参数的要求:
- 它的行为与引用参数类似。
- 使用输出参数时,必须在方法的声明和调用中使用out修饰符。
- 实参必须是变量,在用作实参前必须赋值。因为方法需要内存位置来保存返回值。
- 形参的参数名将作为实参变量的别名,指向同一内存位置。因此在方法执行中发生对形参的改变,在方法结束后也不会消失。
- 与引用参数不同的是、
- 在方法块内,你需要给输出参数赋值之后才能读取它。这意味着参数的初始值是无关的,而且没有必要再方法调用之前赋值。
- 在方法块内,在方法返回之前,代码中每条可能的路径都必须为所有输出参数赋值。
在C#7.0后,我们可以采用 输出参数 新的语法:
3.5 参数数组
我们从上面的参数可以知道,一个形参必须严格对应一个实参。
参数数组则不同,它允许特定类型的零个或多个实参对应一个特定的形参。
参数数组的使用条件:
- 在一个参数列表中只能有一个参数数组,且它必须是列表中最后一个
- 参数数组的所有参数必须是同一类型
声明参数数组时的要求:
- 在数据类型前使用 params修饰符
- 在数据类型后放置一组空的方括号
(关于数组后续章节会讲,这里只需要知道数组就是多个拥有相同数据类型的数据项的集合、我们可以通过一个数字索引来访问数组、数组是引用类型,所以它的所有数据项都在堆里)
参数数组提供实参的两种方式:
- 一个用逗号分隔的该数据类型元素的列表
- void ListInt(params int[] array){...} //方法
- ListInt(1,2,3); //3个int(调用方法)
- 一个该数据类型元素的一维数组
- void ListInt(params int[] array){...} //方法
- int[] intArray=new int[]{1,2,3}; //声明并初始化数组
- ListInt(intArray); //调用方法
注意:
- 对于参数数组,声明方法时要写params修饰符,而调用方法时并不用写params修饰符。
- 对于其他参数类型,值参数声明和调用都不用修饰符、输出参数和引用参数则都需要修饰符。
- 如果实参的参数数组位置没有变量,形参那里则会创建一个有零个元素的数组。
- 参数数组可以作为值参数和引用参数
- 作为值参数,参数数组在方法块内的变化不会保存。
- 作为引用参数,参数数组在方法块内的变化会保存。
3.6 四种参数类型总结
3.7 命名参数
以上四种参数都是位置参数,也就是说每一个实参的位置都必须与相应的形参位置对应。
定义:而C#提供的命名参数,则允许我们只要显示指定参数的名字,就可以以任意顺序在方法调用中列出实参。
命名参数的使用:
- 在方法的声明上没有什么不一样。
- 在方法调用时,形参的名字后面跟着冒号和实际的参数值或表达式
命名参数和位置参数可以一起混合使用:
命名参数可以在我们调用函数的时候提供更多的信息,我们可以给形参提供更有意义的名字。
比如在算正方形面积的方法里给形参的变量名取“height” “long”,当我们调用方法的时候,就可以知道每个实参的含义。为前面编写、后续阅读提供便利。
3.8 可选参数
可选参数允许我们在调用方法的时候包含这个参数,也可以省略它。
为了表明某个参数是可选的,你需要在方法声明中为该参数提供默认值。
可选参数的使用要求:
- 可选参数只能是值参数中的值类型和引用类型,且引用类型默认值只能是null。(也就是说参数不能加修饰符ref、out、params)
- 所有必填参数必须在可选参数之前声明。如果有params参数,则params参数必须在所有可选参数之后声明。

- 可选参数的省略顺序不能随意填写。
- 你必须从可选参数的末尾开始省略。(也就是说,假设有n个可选参数,你可以省略第n个可选参数,或者第n个和第n-1个可选参数,以此类推)
- 你要是想要以随意顺序省略,则可以搭配命名参数使用。
4 方法重载
方法重载(method overloading)的定义:就是一个类中有多个同名的方法。
使用相同方法名称的每个方法必须有一个和其他方法不同的签名(signature)。
- 在方法头中,方法的签名由以下信息组成:
- 方法的名称
- 参数的数目
- 参数的数据类型和顺序
- 参数修饰符
- 返回类型不是签名的一部分。
- 形参的名称也不是签名的一部分。
- 要想使用方法重载,方法的名称必须相同。
//下面是4个加法方法的重载
class MyClass
{
float Add(int a,int b)
{
return a + b;
}
float Add(float a,int b)
{
return a + b;
}
float Add(float a,float b,int c)
{
return a + b + c;
}
float Add(float a,float b)
{
return a + b;
}
}
5 栈帧
我们已经知道了局部变量和参数是位于栈上的,下面深入讨论以下其组织。
栈帧的定义:在调用方法时,内存从栈的顶部开始分配,保存和方法关联的一些数据项。这块内存叫方法的栈帧。
- 栈帧保存的内容包含:
- 返回地址。也就是方法退出的时候继续执行的位置。
- 分配内存的参数。也就是值参数,或者数组参数。
- 和方法调用相关的其他管理数据项。
- 在方法调用时,整个栈帧都会压入栈
- 在方法退出的时候,整个栈帧都会从栈上弹出。这一过程叫栈展开。
6 递归
递归的定义:除了调用其他方法,方法还可以调用自身。
调用方法自身的机制与调用其他方法一样,都是每一次方法调用时把新的栈帧压入栈顶。
示例:
本文深入解析C#中的方法,包括方法的定义、局部变量(var关键字、嵌套块中的局部变量、局部常量)、程序控制流结构、方法调用、返回值(返回语句和void方法)、参数的六种类型(值参数、引用参数、输出参数、参数数组、命名参数、可选参数)以及方法重载。重点阐述了参数的使用细节和方法之间的交互,为理解和应用C#的方法提供了全面指导。
2134

被折叠的 条评论
为什么被折叠?



