传值参数
传值参数也叫值参数,声明的时候不加任何修饰符的形参就是传值形参。可以理解为形参是声明的变量,不过这个变量的初始化是在调用这个方法时所输入的实参
值类型的传值参数
当你把一个值作为实参放进方法的参数列表时,是产生这个值的副本(在内存中新创建一块存储空间),而用这个副本来参与方法体中的运算。因为实际参数和副本(传值形参)所指向的内存位置不同,所以副本与实际参数之间互不影响
class Program
{
static void Main(string[] args)
{
int y =100;
GetAddValue(y);
}
static void GetAddValue(int x)
{
Console.WriteLine(++x);
}
}
//对y赋值为100,把y放进参数列表中。产生一个y的副本,而无论这个副本经过运算之后所得到的值是多少都与y无关,y不会因此改变。```考试,一个学生往旁边学生瞄了一眼看到选择题选b,这个学生也把b抄上去,但后面他越想越不对他觉得这道题应该选a。于是他把b划掉写上a,而旁边学生的答案不会因为这个学生的“运算”而改变```
引用类型的传值参数
引用变量作为实参时,也是产生这个变量的副本,传入方法运算的也是这个副本。哪怕这个副本引用新的实例,变量的数据也不会改变。
因为副本与变量存储的数据是同一个实例的内存地址,所以如果副本改变实例的某些数据,那么变量所引用的实例也是被副本修改了数据的实例。
一般情况下,修改参数所引用的对象的值的操作叫做某个方法的“副作用”
引用参数
使用ref修饰符声明的形参就是引用形参。与值形参不同。引用形参并不创建一个新的存储位置,相反,引用形参表示的存储位置恰是在方法调用作为实参给出的那个变量的存储位置
当形参作为引用参数时,方法调用中的对应实参必须由关键字ref和与形参类型相同的值或变量组成。变量在可以作为引用形参传递之前必须先明确赋值
值类型的引用参数
把一个值类型的值或变量作为实际参数传进方法作为引用形参,引用形参和实际参数指向的是同一个内存地址。所以引用形参在方法中进行运算之后所得到的新值也就是实际参数的新值
class Program
{
static void GetXAdd10Value(ref int x)
{
x+10;
}
static void Main(string[] args)
{
int y = 100;
GetXAdd10Value(y);
Console.WriteLine(y);
}
}
注意:1. 引用参数是不会创建变量的副本
______ 2. 使用ref修饰符显式指出此方法的副作用会使实参的值改变
引用类型的引用参数
引用变量作为传进方法的引用形参时,引用形参所指向的内存地址和引用变量一致。而引用变量所存储的数据是所引用的实例在内存中的地址。如果引用形参引用一个新的实例,那么由于引用变量和引用形参所指向的是同一个内存地址,所以引用变量所引用的实例也会被改变
如果不是给引用变量的引用形参赋新值,而是更改所引用的对象的数据。其效果与传值参数相同。但引用参数和传值参数的机理不同。传值参数的机理是传值形参和实际参数所指向的内存地址不同,但存储的数据是相同的。引用参数的机理是引用形参与实际参数所指向的内存地址是相同的
输出参数
使用out修饰符声明的参数就是输出参数。与引用参数类似输出参数也不会创建实参的副本,输出形参与实参所指向的内存地址一致。
一个方法只能得到一个数据,如果想调用一次方法就能得到多个数据就需要使用输出参数。通过输出参数得到除返回值之外的输出
变量在作为输出形参传入方法之前可以先不用赋值,因为带有输出参数的方法的副作用是得到除返回值之外的输出,所以即使在作为输出形参传进方法之前对其赋值,在方法调用完之后也会对其覆盖,他的作用是装载向外输出的那个数据。然后因为输出参数是为了将方法体内的值输出到方法体之外,所以在方法体内部需要对变量赋值
从语义来看ref是“改变”out是“输出”,使用out修饰符显示告诉此方法副作用是通过参数向外输出值
数组参数
调用一个需要数组的参数的方法往往需要声明一个数组并对其初始化元素如下:
class Program
{
static void Main(string[] args)
{
int[] MyArray = new int[]{1,2,3};//声明数组,初始化元素
int result = CalculayeSum(MyArray);
Console.WriteLine(result);
}
static int CalculateSum(int[] IntArray)
{
int sum = 0 ;
foreach(var time in IntArray)
{
sum += time;
}
return sum;
}
}
使用数组参数可以使代码更加简洁,数组参数声明使用params修饰符,如下:
class Program
{
static void Main(string[] args)
{
int result = CalculateSum(1,2,3);
Console.WriteLine(result);
}
static int CalculateSum(params int[] IntArray)
{
int sum = 0 ;
foreach(var time in IntArray)
{
sum += time;
}
return sum;
}
}
当编译器发现是数组形参时会自动生成一个对应的数组,我们只需要将数组内的元素输入在方法调用的实参里面。
使用数组参数要注意,一个参数列表只能有一个数组参数且声明的时候要写在最后面
具名参数
一般而言,调用方法的时候所输入的实参要与形参所对应。如:static void PrintNameAge(string name int age);
这个方法,在调用的时候实参要按顺序先输入string类型值然后再是int类型值。而使用具名参数可以不用依照形参的顺序写如:PrintNameAge(age:17,name:"lisa");
使用具名参数既可以增强代码的可读性也可以使实参的输入顺序不被形参约束
严格意义上讲具名参数不是参数的种类更像是参数的方法
可选参数
参数具有默认值而变的可选
如果为方法的形参设置了默认值,且在调用方法的时候如果没有输入实参,编译器会用参数的默认值传入方法体当中的运算
不推荐使用可选参数
扩展方法(this参数)
例子:我们有一个double类型的变量x,x的数据是3.14159;我想要把这个值精确到万分位。但是double类型没有这个实现这个功能的方法,而Math类型有一个round的方法。所以就需要调用Math的round方法,但我可不可以用其他方法呢?
class Program
{
double x = 3.14159;
double result = Math.Round(x,4);
Console.WriteLine(result);
}
有一个不现实的想法就是在double类型里新声明一个方法,但我们拿不到double类型的源代码所以无法实现这个想法。这个时候就要介绍扩展方法了
扩展方法用于对目标类型追加方法时使用
示例:
class Program
{
double x = 3.14159;
double result = Math.round(x,4);
Console.WriteLine(result);
}
static class doubleExtenssion
{
publc static double Round(this double input,int digits)
{
double result = Math.Round(input,digits);
return result;
}
}
声明扩展类必须是static静态类根据约定俗成类的标识符需要是"sometypeextension"也就是以追加类型的名字加extension,扩展类的扩展方法必须是静态且公有的。且扩展方法的形参列表第一个参数必须由this修饰