目录
C# 方法
C# 中定义方法
在C#中定义方法时,需要遵循以下结构:
访问修饰符 返回类型 方法名(参数列表)
{
// 方法体
}
下面是对这些部分的详细说明:
- 访问修饰符:用来控制方法的访问权限,可以是 public、private、protected、internal 或 protected internal 等。如果不指定访问修饰符,则默认为 private。
- 返回类型:指定方法返回值的数据类型,如果方法不返回任何内容,可以使用 void 关键字表示。
- 方法名:方法的名称,用于在其他地方调用该方法。
- 参数列表:方法可能会接受零个或多个参数,每个参数由参数类型和参数名称组成,多个参数之间用逗号分隔。
- 方法体:包含了方法的实际实现,即方法被调用时所要执行的代码块。
以下是一个简单的示例,演示了如何在C#中定义一个方法:
public class MyClass
{
// 定义一个方法,无返回值,无参数
public void SayHello()
{
Console.WriteLine("Hello, world!");
}
// 定义一个方法,有返回值,带参数
public int Add(int a, int b)
{
return a + b;
}
}
在这个示例中,我们定义了一个名为 MyClass 的类,并在其中定义了两个方法:SayHello 和 Add。SayHello 方法没有返回值,也没有参数,而 Add 方法接受两个整型参数并返回它们的和。
C# 中调用方法
在 C# 中调用方法很简单,只需按照以下格式编写代码即可:
// 创建类的实例
MyClass myObject = new MyClass();
// 调用无返回值、无参数的方法
myObject.SayHello();
// 调用有返回值、带参数的方法
int result = myObject.Add(3, 4);
Console.WriteLine("The result is: " + result);
在这个示例中,我们首先创建了 MyClass 类的一个实例 myObject。然后我们调用了 SayHello 方法,因为它没有返回值和参数,所以调用方式很简单。接着调用了 Add 方法,并将返回值存储在 result 变量中,最后打印出结果。
需要注意的是,如果方法是静态方法(使用 static 关键字修饰),则可以通过类名直接调用,而不需要创建类的实例。例如:
// 调用静态方法
int result = MyClass.StaticAdd(3, 4);
Console.WriteLine("结果是:: " + result);
在这个示例中,StaticAdd 是一个静态方法,我们直接使用类名 MyClass 来调用它,而不需要创建类的实例。
递归方法调用
在C#中,可以使用递归来实现方法调用自身的功能。递归方法通常用于解决可以被分解为相似子问题的问题,比如树的遍历、阶乘计算等。
以下是一个简单的示例,演示了如何在C#中定义和调用递归方法:
public class RecursionExample
{
// 递归方法计算阶乘
public int Factorial(int n)
{
// 基本情况:当 n 等于 0 或 1 时,阶乘为 1
if (n == 0 || n == 1)
{
return 1;
}
// 递归情况:调用自身来计算 n 的阶乘
else
{
return n * Factorial(n - 1);
}
}
}
class Program
{
static void Main()
{
RecursionExample example = new RecursionExample();
// 调用递归方法计算阶乘
int result = example.Factorial(5);
Console.WriteLine("5的阶乘是: " + result);
}
}
在这个示例中,我们定义了一个 RecursionExample 类,并在其中定义了一个名为 Factorial 的递归方法,用于计算给定整数的阶乘。在 Factorial 方法内部,我们首先检查基本情况(n等于0或1),然后在递归情况下调用自身来计算阶乘。在 Main 方法中,我们创建了 RecursionExample 类的实例,并调用了 Factorial 方法来演示递归调用。
需要注意的是,在使用递归时,务必确保存在终止条件,避免无限循环调用。
参数传递
在C#中,有值参数、引用参数和输出参数这三种向方法传递参数的方式。
值参数
值参数是最常见的参数传递方式。当你向方法传递值参数时,会创建一个新的变量,该变量存储了原始值的副本,而不会影响原始值。对于值参数,形参和实参使用的是两个不同的内存位置。
这种方式确保了实参数据的安全性,因为方法对形参的任何修改都仅限于方法范围内,不会对原始数据产生影响。这也意味着,对形参的任何修改都不会传递回到调用者处。
因此,按值传递参数适用于那些不需要修改原始数据的情况,同时也能确保原始数据的安全。
下面是一个示例:
public void IncrementValue(int number)
{
number++;
}
引用参数
在C#中,按引用传递参数时,方法将直接操作原始数据的内存位置,而不是创建一个副本。
这意味着,当使用引用参数时,对参数的任何修改都会影响到原始数据,因为实际参数和形参指向相同的内存位置。这种方式允许方法能够修改原始数据,并将修改后的值传递回调用者处。
引用参数适用于那些需要对原始数据进行修改并且把修改后的值传递回调用者的情况。与按值传递不同,按引用传递参数能够在方法内部直接影响到原始数据,因此需要谨慎使用,以避免意外的数据修改。
在C#中,通过在参数前面添加 ref 关键字来声明引用参数。下面是一个示例:
public void IncrementValue(ref int number)
{
number++;
}
输出参数
输出参数用于从方法中返回多个值。
在C#中,通过在参数前面添加 out 关键字来声明输出参数。与引用参数不同,输出参数在方法内部不需要对其进行初始化,而是在方法内部为其赋值,这个被赋予的值会传递回到调用者处。
public void GetValues(out int x, out int y)
{
x = 10;
y = 20;
}
使用这三种参数传递方式可以根据需要在方法间传递数据,并灵活地处理方法对数据的修改和返回。
using System;
public class ParameterExample
{
// 值参数示例
public void IncrementValue(int number)
{
number++;
}
// 引用参数示例
public void IncrementValueRef(ref int number)
{
number++;
}
// 输出参数示例
public void GetValues(out int x, out int y)
{
x = 10;
y = 20;
}
}
class Program
{
static void Main()
{
ParameterExample example = new ParameterExample();
// 值参数示例
int value = 10;
example.IncrementValue(value);
Console.WriteLine("值参数方法调用后的值: " + value); // 输出为 10
// 引用参数示例
int refValue = 10;
example.IncrementValueRef(ref refValue);
Console.WriteLine("引用参数方法调用后的值: " + refValue); // 输出为 11
// 输出参数示例
int result1, result2;
example.GetValues(out result1, out result2);
Console.WriteLine("结果1的值: " + result1); // 输出为 10
Console.WriteLine("结果2的值: " + result2); // 输出为 20
}
}
常见题
ref 和 out 的区别
- 使用 ref 和 out 关键字可以将实参的内存地址传递给形参,在函数内部对该地址进行操作,从而修改原始数据。
- 在使用 ref 时,传入函数的参数必须在调用函数之前初始化。而使用 out 时,参数在调用函数之前可以不进行初始化,因为 out 会在函数内部进行初始化。
- 当处理值类型数据时,如果不使用 ref 或 out,则会传递数据的副本,而不是原始数据的地址。因此,对传入的值类型参数进行修改不会影响到原始数据。通过使用 ref 或 out 关键字,可以直接对原始数据进行修改。
- 此外,out 还可以用作模拟多返回值的一种方式,这是 C# 中一个很有用的特性。通过使用 out 参数,函数可以返回多个值,而无需使用复杂的数据结构或者封装。
方法中参数的类型有几种
方法中参数的类型有三种
- int 型参数是通过值传递方式将数值传入方法中,这在 Java 中是最常见的情况。
- ref 型参数传递变量地址给方法,属于引用传递,传递前变量必须初始化。它的特点是有进有出,可以传入方法中的数值并且可以从方法中返回变量值。
- out 型参数类似于 ref 型,也是用于传回结果,但是不需要在方法调用之前对变量进行初始化,而且它只用于从方法中返回值,而无法将数据传入方法中。
扩展方法
在 C# 中,扩展方法允许你向现有的类添加新的方法,而无需修改原始类的定义,也不需要继承该类。这对于在不可修改代码的情况下向现有类型添加功能非常有用。
规则:
- 扩展方法必须定义在静态类中,而且扩展方法本身也必须是静态方法。
- 扩展方法的第一个参数以 this 关键字开头,然后是要扩展的目标类。
- 如果需要接收其他参数,则从第二个参数开始计数,而在实际调用方法时,第一个参数会被隐藏起来,因为它表示调用该扩展方法的对象本身。
using System;
// 静态类中定义扩展方法
public static class StringExtensions
{
// 扩展方法必须是静态方法,并且第一个参数以 this 关键字开头,表示要扩展的目标类型
public static int WordCount(this string str)
{
return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
class Program
{
static void Main()
{
string text = "Hello, how are you?";
int count = text.WordCount(); // 调用扩展方法
Console.WriteLine("字数: " + count); // 输出结果: 字数: 4
}
}
在上面的示例中,我们创建了一个静态类 StringExtensions,其中包含了一个扩展方法 WordCount。这个方法对 string 类型进行了扩展,可以方便地计算字符串中单词的个数。在 Main 方法中,我们演示了如何调用这个扩展方法,并输出了结果。
对于复杂引用类型参数传递的控制
所谓复杂,是参数是数组或集合类型,或者参数包含这些类型数据,这种情况下上面的方法不能保证参数数据不被修改,因为即使对象为只读的,但是对象中的数组或集合字段(属性)还是可以修改的。
1、集合参数(包含集合字段的引用参数也一样)
.net 4.5以前版本可以使用不包含修改集合元素方法的接口来代替具体集合类型。例如使用IEnumerable<T>接口代替List<T>。4.5版本可以直接使用IReadOnlyCollection接口或实现该接口的集合类型。
2、数组参数
没有好的办法保护数组类型参数不被修改,所以尽量避免使用数组类型作为方法参数,除非用到可选参数时候。
更多内容参考:C# 中数组作为参数传递的问题