目录
一、局部变量
1、对比实例字段和局部变量
实例字段 | 局部变量 | |
---|---|---|
生存期 | 实例被创建开始,到实例没有用时结束 | 在块中被声明开始,到块完成执行结束 |
隐式初始化 | 初始化为实例的默认值 | 没有隐式初始化,若不被初始化,编译器报错 |
存储区域 | 因为是类成员,不论是引用类型还是值类型,一律存储在堆内存 | 值类型存储在栈,引用类型存储在堆 |
2、var关键字
- 只能用于局部变量,不能用于字段声明;
- 只能在初始化变量时使用;
- 一旦编译器推断出类型,就固定且不可改变;
- 不像JavaScript中的var可以引用不同的类型,C#的var是从表达式右边推断出变量类型,因此var并不改变C#的强类型性质。
二、局部常量
1、特征
- 在声明时必须初始化;
- 声明后不能改变;
2、格式
其中,const是关键字,Value初始化值是必须的。
初始化值是在编译时期决定的,可以是null引用,但不能是某对象的引用,因为对象的引用是在运行时决定的。
三、控制流
跳转语句
- break 跳出当前循环
- continue 到当前循环的顶部
- goto 到一个命名的语句
- return 返回到调用方法继续执行
四、局部函数
在一个方法内部嵌套的另一个方法就是局部函数。
与局部变量不同,局部函数可写在使用处的任何地方,也就是说,使用局部函数的代码可以先于局部函数的定义。
class LocalFunction
{
public void MethodWithLocalFunction()
{
int result = MyLocalFunction(6);
Console.WriteLine($"Results of local function call:{result}");
int MyLocalFunction(int z1) //声明局部函数
{
return z1 * 5;
}
}
}
五、参数
1、形参
形参是局部变量,声明在方法参数列表而不是方法体中。
- 形参是变量,因此可以读写;
- 与局部变量不同,形参方法体外面已定义,在方法体开始之前初始化,输出参数除外;
- 参数列表中可以有任意数目的形参,彼此以逗号隔开;
2、实参
实参是初始化形参的表达式或变量。
- 实参位于调用函数的参数列表中;
- 实参必须与对应位置的形参类型保持一致,或者编译器能够推断出实参类型与对应位置形参类型一致;
位置参数
实参的数量必须与形参保持一致,且对应位置的类型也必须保持一致,这种形式的参数称为位置参数。
class LocationParameter
{
public int Sum(int x,int y)
{
return x + y;
}
public float Avg(float f1,float f2)
{
return (f1 + f2) / 2;
}
public void ConsoleOut()
{
int someInt = 6;
Console.WriteLine($"Newflash: Sum:5 + {someInt} = {Sum(5, 6)}");
Console.WriteLine($"Newflash: Avg:(5 + {someInt})/2 = {Avg(5, 6)}");
}
}
其中,调用Avg函数时,编译器已经将5和someInt隐式转换为float类型了。
六、值参数
值参数是把实参的值复制给形参。
方法被调用时执行如下操作:
- 在栈中为形参分配空间;
- 将实参的值复制给形参;
注意:值类型与值参数没有关系,值类型是指类型本身包含其值。
class MyValueParameterClass
{
public int Val = 20;
}
public class ValueParameter
{
static void MyMethod(MyValueParameterClass f1,int f2)
{
f1.Val = f1.Val + 5;
f2 = f2 + 5;
Console.WriteLine($"f1.Val={f1.Val}, f2={f2}");
}
static void Main()
{
MyValueParameterClass a1 = new MyValueParameterClass();
int a2 = 10;
MyMethod(a1, a2);
Console.WriteLine($"a1.Val={a1.Val}, a2={a2}");
}
}
执行结果:
上图展示了实参和形参在方法执行的不同阶段的值,表明如下3点:
- 在方法调用前,用作实参的变量a2已经在栈里了;
- 在方法开始时,系统在栈中为形参分配空间,并从实参复制值
- 因为a1是引用类型的,所以引用被复制,结果实参和形参都引用堆中的同一个对象;
- 因为a2是值类型的,所以值被复制,产生了一个独立的数据项;
- 在方法的结尾,f2和对象f1的字段都被加上了5
- 方法执行后,形参从栈中弹出;
- a2,值类型,它的值不受方法行为的影响;
- a1,引用类型,但它的值被方法的行为改变了;
七、引用参数
使用引用参数时,必须在方法的声明和调用中都使用ref修饰符。
实参必须是变量,在用作实参前必须被赋值。如果是引用类型变量,可以赋值为一个引用或null。
与值参数相比,引用参数有以下特点:
- 不会在栈上为形参分配内存;
- 形参的参数名将作为实参变量的别名,指向相同的内存位置。
class MyRefParameterClass
{
public int Val = 20;
}
class RefParameter
{
static void MyMethod(MyRefParameterClass f1,int f2)
{
f1.Val = f1.Val + 5;
f2 = f2 + 5;
Console.WriteLine($"f1.Val={f1.Val}, f2={f2}");
}
static void Main()
{
MyRefParameterClass a1 = new MyRefParameterClass();
int a2 = 10;
MyMethod(a1, a2);
Console.WriteLine($"a1.Val={a1.Val}, a2={a2}");
}
}
执行结果:
上图阐明了引用参数在方法执行的不同阶段实参和形参的值:
- 在方法调用之前,将要被用作实参的变量a1和a2已经在栈里了;
- 在方法的开始,形参名被设置为实参的别名,变量a1和f1引用相同的内存位置,a2和f2引用相同的内存位置;
- 在方法的结束位置,f2和f1的对象的字段都被加上了5;
- 方法执行之后,形参的名称已经失效,但是值类型a2的值和引用类型a1所指向的对象的值都被方法内的行为改变了;
八、引用类型作为值参数和引用参数
1、将引用类型作为值参数传递
如果在方法内创建一个新对象并赋值给形参,将切断形参与实参之间的关联,并且在方法调用结束后,新对象也将不复存在。
class MyRefAsValueParamClass
{
public int Val = 20;
}
class RefAsValueParam
{
static void MyMethod(MyRefAsValueParamClass f1)
{
f1.Val = 50;