目录

第三章 方法

3.1 方法基础

什么是方法

方法(Method)是执行特定任务的代码块,是组织和重用代码的基本单元。通过方法,我们可以将复杂的程序分解为可管理的小块。

方法的基本结构

[访问修饰符] [返回类型] 方法名([参数列表])
{
    // 方法体
    // 执行语句
    [return 返回值;]
}

方法定义示例

// 无参数无返回值
void SayHello()
{
    Console.WriteLine("Hello, World!");
}
 
// 有参数无返回值
void Greet(string name)
{
    Console.WriteLine($"Hello, {name}!");
}
 
// 有参数有返回值
int Add(int a, int b)
{
    return a + b;
}
 
// 无参数有返回值
string GetCurrentTime()
{
    return DateTime.Now.ToString("HH:mm:ss");
}

3.2 参数传递

值传递

默认情况下,C#使用值传递。方法接收的是参数的副本,修改不影响原始值。

void Increment(int number)
{
    number++;  // 只修改副本
    Console.WriteLine($"方法内部: {number}");
}
 
int value = 10;
Increment(value);
Console.WriteLine($"方法外部: {value}");  // 仍然是10

引用传递(ref)

使用ref关键字,方法可以修改原始变量的值。

void IncrementByRef(ref int number)
{
    number++;  // 修改原始值
}
 
int value = 10;
IncrementByRef(ref value);
Console.WriteLine(value);  // 输出11

输出参数(out)

out参数用于从方法返回多个值,调用前不需要初始化。

void Calculate(int a, int b, out int sum, out int difference)
{
    sum = a + b;
    difference = a - b;
}
 
int s, d;
Calculate(10, 5, out s, out d);
Console.WriteLine($"和: {s}, 差: {d}");
 
// 丢弃不需要的输出
Calculate(10, 5, out int sum, out _);

in参数(C# 7.2+)

in参数提供只读的引用传递,提高大型结构体的传递效率。

void Display(in Point point)
{
    Console.WriteLine($"X: {point.X}, Y: {point.Y}");
    // point.X = 10;  // 编译错误!不能修改in参数
}
 
struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

参数数组(params)

params允许传递可变数量的参数。

int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int num in numbers)
    {
        total += num;
    }
    return total;
}
 
// 多种调用方式
Console.WriteLine(Sum(1, 2, 3));           // 6
Console.WriteLine(Sum(1, 2, 3, 4, 5));     // 15
Console.WriteLine(Sum());                   // 0
Console.WriteLine(Sum(new[] { 1, 2, 3 })); // 6

命名参数和可选参数

// 可选参数(带默认值)
void PrintInfo(string name, int age = 18, string city = "北京")
{
    Console.WriteLine($"{name}, {age}岁, 来自{city}");
}
 
// 调用
PrintInfo("张三");                          // 使用默认值
PrintInfo("李四", 25);                      // 指定年龄
PrintInfo("王五", city: "上海");            // 使用命名参数跳过前面的参数
PrintInfo(name: "赵六", age: 30, city: "广州"); // 全部使用命名参数

3.3 返回值

单一返回值

int Multiply(int a, int b)
{
    return a * b;
}
 
string GetFullName(string firstName, string lastName)
{
    return $"{lastName} {firstName}";
}

元组返回多个值(C# 7+)

// 返回元组
(int sum, int difference) Calculate(int a, int b)
{
    return (a + b, a - b);
}
 
// 调用
var result = Calculate(10, 5);
Console.WriteLine($"和: {result.sum}, 差: {result.difference}");
 
// 解构
(int s, int d) = Calculate(10, 5);

引用返回值(C# 7+)

int[] numbers = { 1, 2, 3, 4, 5 };
 
ref int Find(int[] arr, int target)
{
    for (int i = 0; i < arr.Length; i++)
    {
        if (arr[i] == target)
        {
            return ref arr[i];
        }
    }
    throw new InvalidOperationException("未找到");
}
 
ref int found = ref Find(numbers, 3);
found = 30;  // 修改数组中的值
Console.WriteLine(string.Join(", ", numbers));  // 1, 2, 30, 4, 5

3.4 方法重载

重载规则

方法重载允许在同一个类中定义多个同名方法,只要它们的参数列表不同。

class Calculator
{
    // 重载Add方法
    public int Add(int a, int b)
    {
        return a + b;
    }
 
    public double Add(double a, double b)
    {
        return a + b;
    }
 
    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
 
    public string Add(string a, string b)
    {
        return a + b;
    }
}
 
// 使用
var calc = new Calculator();
Console.WriteLine(calc.Add(1, 2));           // 3
Console.WriteLine(calc.Add(1.5, 2.5));       // 4.0
Console.WriteLine(calc.Add(1, 2, 3));        // 6
Console.WriteLine(calc.Add("Hello, ", "World")); // "Hello, World"

重载决策

class OverloadDemo
{
    public void Method(int x) => Console.WriteLine("int");
    public void Method(long x) => Console.WriteLine("long");
    public void Method(double x) => Console.WriteLine("double");
    public void Method(object x) => Console.WriteLine("object");
}
 
var demo = new OverloadDemo();
demo.Method(5);      // int - 精确匹配
demo.Method(5L);     // long
demo.Method(5.0);    // double
demo.Method("text"); // object

3.5 递归

递归基础

递归是方法调用自身的编程技巧,必须包含终止条件。

// 阶乘计算
int Factorial(int n)
{
    if (n <= 1)  // 终止条件
        return 1;
 
    return n * Factorial(n - 1);
}
 
Console.WriteLine(Factorial(5));  // 120 (5! = 5*4*3*2*1)

斐波那契数列

int Fibonacci(int n)
{
    if (n <= 1)
        return n;
 
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}
 
// 输出前10个斐波那契数
for (int i = 0; i < 10; i++)
{
    Console.Write(Fibonacci(i) + " ");
}
// 输出: 0 1 1 2 3 5 8 13 21 34

递归 vs 迭代

// 递归版本
int FactorialRecursive(int n)
{
    if (n <= 1) return 1;
    return n * FactorialRecursive(n - 1);
}
 
// 迭代版本(更高效)
int FactorialIterative(int n)
{
    int result = 1;
    for (int i = 2; i <= n; i++)
    {
        result *= i;
    }
    return result;
}

3.6 局部函数(C# 7+)

基本用法

int CalculateFactorial(int n)
{
    // 局部函数
    int Factorial(int x)
    {
        if (x <= 1) return 1;
        return x * Factorial(x - 1);
    }
 
    if (n < 0)
        throw new ArgumentException("n必须非负");
 
    return Factorial(n);
}

捕获变量

void OuterMethod(int factor)
{
    // 局部函数可以访问外部方法的变量
    int Multiply(int x) => x * factor;
 
    Console.WriteLine(Multiply(5));  // 5 * factor
    Console.WriteLine(Multiply(10)); // 10 * factor
}

3.7 匿名方法与Lambda表达式

匿名方法(C# 2.0)

// 已较少使用,主要用于兼容性
delegate void MyDelegate(string message);
 
MyDelegate del = delegate(string msg)
{
    Console.WriteLine(msg);
};
 
del("Hello from anonymous method");

Lambda表达式(C# 3.0+)

// 表达式Lambda
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));  // 25
 
// 语句Lambda
Action<string> greet = name =>
{
    Console.WriteLine($"Hello, {name}!");
    Console.WriteLine("Welcome!");
};
 
greet("Alice");
 
// 多参数Lambda
Func<int, int, int> add = (a, b) => a + b;
 
// 无参数Lambda
Action sayHello = () => Console.WriteLine("Hello!");

3.8 扩展方法

定义扩展方法

// 必须定义在静态类中
public static class StringExtensions
{
    // 使用this关键字
    public static bool IsNullOrEmpty(this string str)
    {
        return string.IsNullOrEmpty(str);
    }
 
    public static string Reverse(this string str)
    {
        char[] chars = str.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
 
    public static int WordCount(this string str)
    {
        return str.Split(new[] { ' ', '\t', '\n' }, 
            StringSplitOptions.RemoveEmptyEntries).Length;
    }
}
 
// 使用
string text = "Hello World";
Console.WriteLine(text.Reverse());      // dlroW olleH
Console.WriteLine(text.WordCount());    // 2

3.9 方法实战

数值计算工具类

public static class MathUtils
{
    // 计算数组平均值
    public static double Average(params double[] numbers)
    {
        if (numbers.Length == 0)
            throw new ArgumentException("数组不能为空");
 
        return numbers.Average();
    }
 
    // 判断素数
    public static bool IsPrime(int number)
    {
        if (number < 2) return false;
        if (number == 2) return true;
        if (number % 2 == 0) return false;
 
        for (int i = 3; i <= Math.Sqrt(number); i += 2)
        {
            if (number % i == 0)
                return false;
        }
        return true;
    }
 
    // 计算最大公约数
    public static int GCD(int a, int b)
    {
        while (b != 0)
        {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
 
    // 计算最小公倍数
    public static int LCM(int a, int b)
    {
        return Math.Abs(a * b) / GCD(a, b);
    }
}

字符串处理工具类

public static class StringUtils
{
    // 截断字符串
    public static string Truncate(this string str, int maxLength, 
        string suffix = "...")
    {
        if (string.IsNullOrEmpty(str) || str.Length <= maxLength)
            return str;
 
        return str.Substring(0, maxLength - suffix.Length) + suffix;
    }
 
    // 驼峰命名转换
    public static string ToCamelCase(this string str)
    {
        if (string.IsNullOrEmpty(str)) return str;
 
        var words = str.Split(new[] { ' ', '_', '-' }, 
            StringSplitOptions.RemoveEmptyEntries);
 
        return words[0].ToLower() + 
            string.Concat(words.Skip(1).Select(w => 
                char.ToUpper(w[0]) + w.Substring(1).ToLower()));
    }
 
    // 安全的转换为整数
    public static int? ToIntOrNull(this string str)
    {
        return int.TryParse(str, out int result) ? result : (int?)null;
    }
}

练习题

基础练习

1. 基础方法:编写一个方法IsEven,判断一个整数是否为偶数,返回布尔值。

2. 参数传递:解释以下代码的输出结果,并说明原因:

 <code csharp>
 void Swap(int a, int b)
 {
     int temp = a;
     a = b;
     b = temp;
 }
 int x = 5, y = 10;
 Swap(x, y);
 Console.WriteLine($"x={x}, y={y}");
 </code>
 然后使用ref关键字修改方法,使交换真正生效。

3. 方法重载:为Calculate类添加重载方法,支持加法、减法、乘法和除法运算,分别处理int和double类型。

进阶练习

4. 递归实现:使用递归实现以下功能:

  1. 计算1+2+3+…+n的和
  2. 计算x的n次幂
  3. 反转字符串

5. 多返回值:编写一个方法AnalyzeNumber,接收一个整数,返回:

  1. 是否为偶数
  2. 是否为素数
  3. 各位数字之和

使用元组返回多个值。

6. 扩展方法:为DateTime类型添加扩展方法:

  1. IsWeekend() - 判断是否为周末
  2. StartOfWeek() - 获取本周开始日期
  3. ToFriendlyString() - 返回友好的时间描述(如“今天”、“昨天”、“3天前”等)

挑战练习

7. 表达式计算器:实现一个简单的方法,可以解析并计算形如“1+2*3”的简单数学表达式(支持+、-、*、/和括号)。

8. 排列组合:编写方法计算n个元素的排列数和组合数,使用递归和迭代两种实现方式。

9. 方法链式调用:设计一个Fluent API的StringBuilder类,支持链式调用,例如:

 <code csharp>
 var result = new FluentBuilder()
     .Append("Hello")
     .AppendLine()
     .Append("World")
     .Replace("World", "C#")
     .ToString();
 </code>

本章小结

本章学习了C#方法的核心概念:

  1. 方法的定义和调用
  2. 各种参数传递方式(值、ref、out、in、params)
  3. 可选参数和命名参数
  4. 单一返回值和多元组返回值
  5. 方法重载的规则
  6. 递归的概念和应用
  7. 局部函数的使用
  8. Lambda表达式
  9. 扩展方法

掌握这些方法技巧后,你可以编写出结构清晰、可重用性高的代码。