参数是方法的一部分。
- 传值参数:参数的默认传递方式。
- 输出参数:用于除返回值外还需输出的场景。
- 引用参数:用于需要修改实际参数值的场景。
- 数组参数:用于简化方法的调用。
- 具名参数:提高可读性。
- 可选参数:参数拥有默认值。
- 扩展方法(this参数):为目标数据类型“追加”方法。
1、传值参数:值类型
作用域为当前方法:初始值为该方法调用时所赋给他的实参。
2、传值参数:引用类型,并且新创建对象
引用类型的变量:存储的值是一个地址。指向在堆内存中的数据。即:引用类型的变量的值存储的是引用类型实例在堆内存中的地址。
引用类型的变量和实例是分开的。引用类型的变量会引用着引用类型的实例。
3、引用参数:值类型
使用ref修饰符声明的形参。
- 传值参数会为传入方法内实际参数创建一个副本。
- 引用参数不会为传入的参数创建副本。引用参数会直接指向所传入的实际参数所指向的内存地址。
引用参数并不创建新的存储位置,相反引用参数表示的存储位置恰是在方法调用中作为实参给出的那个变量所表示的存储位置。因此:变量在可以作为引用形参传递之前,必须先明确赋值。
引用参数是故意改变传入的实参的值。利用方法的副作用SideEffect改变实际参数的值。
static void Main(string[] args)
{
int y = 1;
IWantSideEffect(ref y);
Console.WriteLine(y); // 101
}
static void IWantSideEffect(ref int x)
{
x = x + 100;
}
4、引用类型:引用类型,创建新对象
class Program
{
static void Main(string[] args)
{
Student outterStu = new Student() { Name = "Tim" };
Console.WriteLine("HashCode = {0}, Name = {1}", outterStu.GetHashCode(), outterStu.Name);
Console.WriteLine("-------------------------");
IWantSideEffect(ref outterStu);
Console.WriteLine("HashCode = {0}, Name = {1}", outterStu.GetHashCode(), outterStu.Name);
}
static void IWantSideEffect(ref Student stu)
{
stu = new Student() { Name = "Tom" };
Console.WriteLine("HashCode = {0}, Name = {1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
输出:
ref关键字,会把实际对象传入,并修改。
5、引用参数:引用类型,不创建新对象只改变对象值
此处仅修改值,并不会修改地址。
class Program
{
static void Main(string[] args)
{
Student outterStu = new Student() { Name = "Tim" };
Console.WriteLine("HashCode = {0}, Name = {1}", outterStu.GetHashCode(), outterStu.Name);
Console.WriteLine("-------------------------");
SomeSideEffect(ref outterStu);
Console.WriteLine("HashCode = {0}, Name = {1}", outterStu.GetHashCode(), outterStu.Name);
}
static void SomeSideEffect(ref Student stu)
{
stu.Name = "Tom" ;
Console.WriteLine("HashCode = {0}, Name = {1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
6、输出参数→值类型
用out修饰符声明的形参是输出形参,一定是有副作用的。类似于引用形参,输出形参不创建新的存储位置。
把数据看做产品,把方法看做产品加工厂,正常情况,方法只能返回一个值。return。如果想一次性返回多个值,需要用到out。获得除返回值以外,额外的输出。
输出参数,方法通过这个参数往外输出。所以在方法返回之前,该方法的每个输出形参都必须明确赋值。
string arg1 = Console.ReadLine();
double x = 0;
bool b1 = double.TryParse(arg1, out x);
if (b1==false)
{
Console.WriteLine("input error!");
return;
}
else
{
Console.WriteLine(x);
}
系统再带的TryParse第二个参数是out类型。
自定义out参数方法。
class Program
{
static void Main(string[] args)
{
double x = 0;
bool b = DoubleParser.TryParse("798",out x);
if (b==true)
{
Console.WriteLine(x+1);
}
}
}
class DoubleParser
{
/// <summary>
/// 字符串转double,如果成功返回一个bool类型值。同时输出转换结果
/// </summary>
/// <param name="input"></param>
/// <param name="result">out类型,输出转换结果</param>
/// <returns></returns>
public static bool TryParse(string input, out double result)
{
try
{
result = double.Parse(input);
return true;
}
catch
{
result = 0;
return false;
}
}
}
7、输出参数→引用类型
out修饰的是自定义类。
class Program
{
static void Main(string[] args)
{
Student stu = null;
bool b = StudentFactory.Create("Tim", 34,out stu);
if (b==true)
{
Console.WriteLine("Studeng:{0}, age is {1}.",stu.Name,stu.Age);
}
}
}
class Student
{
public int Age { get; set; }
public string Name { get; set; }
}
class StudentFactory
{
/// <summary>
/// 创造一个学生对象,如果年龄大于18小于80,创建对象,返回bool值,输出对象。
/// </summary>
/// <param name="stuName"></param>
/// <param name="stuAge"></param>
/// <param name="result"></param>
/// <returns></returns>
public static bool Create(string stuName, int stuAge,out Student result)
{
result = null;
if (string.IsNullOrEmpty(stuName))
{
return false;
}
if (stuAge<20||stuAge>80)
{
return false;
}
result = new Student() { Name = stuName, Age = stuAge };
return true;
}
}
8、数组参数
必须是形参列表中的最后一个,有params修饰。且只能有一个参数是params的。
比如:Sting.Format方法和String.Split方法。
static void Main(string[] args)
{
int[] myArray = new int[] { 1, 3, 4, 2 };
int result = CalculateSum(myArray);
Console.WriteLine(result);
int result2 = CalculateSum(1, 2, 3, 4, 5, 6, 7);
Console.WriteLine(result2);
}
static int CalculateSum(params int[] intArray)
{
int sum = 0;
foreach (var item in intArray)
{
sum += item;
}
return sum;
}
带有params之后参数除了支持直接输入数组类型以外,还可以直接输入一组数。
其实在C#中很多这种应用:
比如:最常用的打印函数:这也是为何我们可以输入任意个数的参数的原因。
比如:字符串中的实例方法split。
9、具名参数:参数的使用方法,而非类别
static void Main(string[] args)
{
//不具名调用;
PrintInfo("Tim", 34);
// 具名调用:
PrintInfo(name: "Tim", age: 34);
PrintInfo(age: 34, name: "Tim");
}
static void PrintInfo(string name,int age)
{
Console.WriteLine("Hello {0}, you are {1}.",name, age);
}
1、代码更直观;
2、参数顺序不怕乱;
10、可选参数:
参数因为具有默认值而变得“可选”。因为声明时已经赋值,所以调用时可写可不写。
11、扩展方法(this参数)Extension Methods
- 方法必须是公有的、静态的,即被public static所修饰。
- 必须是形参列表中的第一个,由this修饰。
- 必须由一个静态类(一般类名为SomeTypeExtension)来统一收纳对SomeType类型的扩展方法
- 举例:LINQ方法。
class Program
{
static void Main(string[] args)
{
double x = 3.14159;
//double y = Math.Round(x, 4);
double y = x.Round(4);
Console.WriteLine(y);
}
}
// 对Double类型的扩展。那么double类可以直接通过成员访问的方式调用。
static class DoubleExtension
{
// 方法的第一个参数加上this,就可以用点直接点.出来。(点)成员访问修饰符。
public static double Round(this double input, int digits)
{
double result = Math.Round(input, digits);
return result;
}
}