0%

实际参数和形式参数的定义,以及 C# 中的形式参数的分类

本文主要记录实际参数和形式参数的定义,以及 C# 中的形式参数的分类。

实际参数 argument

简称“实参”。

在调用有参方法时,方法名后面括号中的参数称为“实际参数”。

形式参数 parameter

简称“形参”。

在定义方法名和方法体的时候使用的参数称为“形式参数”,目的是用来接收调用该方法时传入的参数。

C# 中的形式参数

传值参数

传值参数会创建实参的副本来接收实参的值。

对形参的修改不影响实参的值。

参数类型为值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Program
{
static void Main(string[] args)
{
int value = 8;
Method(value);
Console.WriteLine(value); // value 的值不变
}

static void Method(int value)
{
value = 16;
}
}

参数类型为引用类型

创建新对象:这种情况比较少见,一般不会用到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Program
{
static void Main(string[] args)
{
Student student = new Student() { Name = "daixx" };
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
Method(student);
// 对比哈希码可知,student 的值没有发生变化
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
}

static void Method(Student student)
{
student = new Student() { Name = "daixxTech" };
}
}

class Student
{
public string Name { get; set; }
}

不创建新对象,只操作对象:这种情况也比较少见,因为作为方法而言,主要输出应该靠返回值,对形参所引用的对象进行操作属于方法的副作用(side-effect),这种副作用在平时编程时要尽量避免。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Program
{
static void Main(string[] args)
{
Student student = new Student() { Name = "daixx" };
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
Method(student);
// student.Name 的值被修改为 daixxTech
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
}

static void Method(Student student)
{
student.Name = "daixxTech";
}
}

class Student
{
public string Name { get; set; }
}

引用参数

对形参的修改会影响实参的值。

在对应的形参和实参前面加 ref 关键字进行修饰。

参数类型为值类型

即有意利用方法的副作用来修改实参的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Program
{
static void Main(string[] args)
{
int value = 8;
Method(ref value);
Console.WriteLine(value); // value 的值被修改为 16
}

static void Method(ref int value)
{
value = 16;
}
}

参数类型为引用类型

创建新对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void Main(string[] args)
{
Student student = new Student() { Name = "daixx" };
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
Method(ref student);
/* 执行完方法后,student 被修改为新的 student。
新的 student 的 Name 属性的值为 daixxTech */
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
}

static void Method(ref Student student)
{
student = new Student() { Name = "daixxTech" };
}

不创建新对象,只操作对象:与传值参数(即不加 ref 关键字)的结果相同,但是实现机理不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Program
{
static void Main(string[] args)
{
Student student = new Student() { Name = "daixx" };
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
Method(ref student);
// student 不变,student.Name 的值被修改为 daixxTech
Console.WriteLine("{0}, {1}", student.GetHashCode(), student.Name);
}

static void Method(ref Student student)
{
student.Name = "daixxTech";
}
}

输出参数

对形参的修改会影响实参的值。

在对应的形参和实参前面加 out 关键字进行修饰。

方法体内必须要对输出参数进行赋值操作。

从语义上来讲,ref 是为了“改变”,out 是为了“输出”。

参数类型为值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Program
{
static void Main(string[] args)
{
int value = 8;
// Parse 失败,value 的值被修改为 0
if (IntParser.TryParse("abc", out value))
{
Console.WriteLine("Success, " + value);
}
else
{
Console.WriteLine("Fail, " + value);
}
// Parse 成功,value 的值被修改为 16
if (IntParser.TryParse("16", out value))
{
Console.WriteLine("Success, " + value);
}
else
{
Console.WriteLine("Fail, " + value);
}
}
}

class IntParser
{
public static bool TryParse(string input, out int output)
{
try
{
output = int.Parse(input);
return true;
}
catch
{
output = 0;
return false;
}
}
}

参数类型为引用类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Program
{
static void Main(string[] args)
{
Student student = null;
if (StudentFactory.Create("daixx", 20, out student))
{
// student 的值被修改为新的 student(Name 的值为 daixx,Age 的值为 20)
Console.WriteLine("{0}, {1}.", student.Name, student.Age);
}
}
}

class Student
{
public int Age { get; set; }

public string Name { get; set; }
}

class StudentFactory
{
public static bool Create(string name, int age, out Student output)
{
output = null;
if (string.IsNullOrEmpty(name))
{
return false;
}
if (age < 0 || age > 120)
{
return false;
}
output = new Student() { Name = name, Age = age };
return true;
}
}

数组参数

只能有一个,且必须是形参列表的最后一个,用 params 关键字进行修饰。

Console.WriteLine() 方法就是如此实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
static void Main(string[] args)
{
int sum = CalculateSum(1, 2, 3); // 不需要传入数组参数,方法自动将参数打包为数组
Console.WriteLine(sum); // 输出为 6
}

static int CalculateSum(params int[] intArray)
{
int sum = 0;
foreach (var item in intArray)
{
sum += item;
}
return sum;
}
}

具名参数

即参数具有名字。

具名参数的优点:

  1. 提高代码可读性
  2. 参数的位置不在受参数列表约束
1
2
3
4
5
6
7
8
9
10
11
12
13
class Program
{
static void Main(string[] args)
{
PrintInfo("daixx", 20); // 不具名参数
PrintInfo(age: 20, name: "daixx"); // 具名参数
}

static void PrintInfo(string name, int age)
{
Console.WriteLine("{0}, {1}.", name, age);
}
}

可选参数

参数具有默认值,从而变为可选参数。

调用方法时不传入该参数,则该参数自动获得默认值。

不推荐使用可选参数。

1
2
3
4
5
6
7
8
9
10
11
12
class Program
{
static void Main(string[] args)
{
PrintInfo();
}

static void PrintInfo(string name = "daixx", int age = 20)
{
Console.WriteLine("{0}, {1}.", name, age);
}
}

扩展方法(this 参数)

扩展方法必须是共有的,静态的,即被 public static 关键字进行修饰。

必须是形参列表的第一个,用 this 关键字进行修饰。

必须由一个静态类(一般命名为 SomeTypeExtension)来统一收纳对 SomeType 类型的扩展方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Program
{
static void Main(string[] args)
{
double x = 3.14159;
// double 类型本身没有 Round 方法,只能使用 Math.Round
//double y = Math.Round(x, 4);
// 使用了扩展方法后,double 就可以通过 this 参数调用 Round 方法了
double y = x.Round(4);
Console.WriteLine(y);
}
}

static class DoubleExtension
{
public static double Round(this double input, int digits)
{
return Math.Round(input, digits);
}
}

LINQ

LINQ 就是扩展方法的一大应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Linq; // 引入Linq命名空间

class Program
{
static void Main(string[] args)
{
var myList = new List<int>() { 11, 12, 9, 14, 15 };
//bool result = AllGreaterThanTen(myList);
// 这里的 All 就是一个扩展方法,用 1 行代码解决了 10+ 行代码要做的事
bool result = myList.All(i => i > 10);
Console.WriteLine(result);
}

static bool AllGreaterThanTen(List<int> intList)
{
foreach (var item in intList)
{
if (item <= 10)
{
return false;
}
}
return true;
}
}