Skip to content

C# 方法和函数

本章将详细介绍 C# 中的方法(函数),包括方法的定义、调用、参数传递、重载等核心概念,帮助你编写模块化和可重用的代码。

方法基础

方法的定义

csharp
// 基本语法
访问修饰符 返回类型 方法名(参数列表)
{
    // 方法体
    return 返回值;  // 如果返回类型不是 void
}

// 示例:无参数无返回值的方法
public static void SayHello()
{
    Console.WriteLine("Hello, World!");
}

// 示例:有参数有返回值的方法
public static int Add(int a, int b)
{
    return a + b;
}

// 示例:有参数无返回值的方法
public static void PrintMessage(string message)
{
    Console.WriteLine($"消息: {message}");
}

方法的调用

csharp
class Program
{
    static void Main(string[] args)
    {
        // 调用无参数方法
        SayHello();
        
        // 调用有参数有返回值的方法
        int result = Add(5, 3);
        Console.WriteLine($"5 + 3 = {result}");
        
        // 调用有参数无返回值的方法
        PrintMessage("这是一条测试消息");
        
        // 直接在表达式中使用方法返回值
        Console.WriteLine($"10 + 20 = {Add(10, 20)}");
    }
    
    static void SayHello()
    {
        Console.WriteLine("Hello, World!");
    }
    
    static int Add(int a, int b)
    {
        return a + b;
    }
    
    static void PrintMessage(string message)
    {
        Console.WriteLine($"消息: {message}");
    }
}
```###
 返回类型

```csharp
// void - 无返回值
static void PrintInfo()
{
    Console.WriteLine("这个方法没有返回值");
}

// 基本数据类型返回值
static int GetAge()
{
    return 25;
}

static double CalculateArea(double radius)
{
    return Math.PI * radius * radius;
}

static bool IsEven(int number)
{
    return number % 2 == 0;
}

static string GetFullName(string firstName, string lastName)
{
    return $"{firstName} {lastName}";
}

// 数组返回值
static int[] GetNumbers()
{
    return new int[] { 1, 2, 3, 4, 5 };
}

// 对象返回值
static DateTime GetCurrentTime()
{
    return DateTime.Now;
}

参数传递

值传递(默认方式)

csharp
static void ModifyValue(int x)
{
    x = 100;  // 只修改局部副本
    Console.WriteLine($"方法内 x = {x}");
}

static void Main()
{
    int number = 10;
    Console.WriteLine($"调用前 number = {number}");
    
    ModifyValue(number);
    
    Console.WriteLine($"调用后 number = {number}");  // 仍然是 10
}

引用传递 (ref)

csharp
static void ModifyByRef(ref int x)
{
    x = 100;  // 修改原始变量
    Console.WriteLine($"方法内 x = {x}");
}

static void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

static void Main()
{
    int number = 10;
    Console.WriteLine($"调用前 number = {number}");
    
    ModifyByRef(ref number);
    
    Console.WriteLine($"调用后 number = {number}");  // 变成 100
    
    // 交换两个变量
    int x = 5, y = 15;
    Console.WriteLine($"交换前: x = {x}, y = {y}");
    Swap(ref x, ref y);
    Console.WriteLine($"交换后: x = {x}, y = {y}");
}

输出参数 (out)

csharp
static bool TryDivide(int dividend, int divisor, out double result)
{
    if (divisor == 0)
    {
        result = 0;  // out 参数必须赋值
        return false;
    }
    
    result = (double)dividend / divisor;
    return true;
}

static void GetNameParts(string fullName, out string firstName, out string lastName)
{
    string[] parts = fullName.Split(' ');
    firstName = parts.Length > 0 ? parts[0] : "";
    lastName = parts.Length > 1 ? parts[1] : "";
}

static void Main()
{
    // 使用 out 参数
    if (TryDivide(10, 3, out double result))
    {
        Console.WriteLine($"除法结果: {result:F2}");
    }
    else
    {
        Console.WriteLine("除法失败");
    }
    
    // 内联声明 out 变量
    if (TryDivide(15, 0, out double failResult))
    {
        Console.WriteLine($"结果: {failResult}");
    }
    else
    {
        Console.WriteLine("除零错误");
    }
    
    // 多个 out 参数
    GetNameParts("John Doe", out string first, out string last);
    Console.WriteLine($"名: {first}, 姓: {last}");
}

输入参数 (in) - C# 7.2+

csharp
// in 参数用于大型结构体,避免复制开销
struct LargeStruct
{
    public int Value1;
    public int Value2;
    public int Value3;
    // ... 更多字段
}

static void ProcessLargeStruct(in LargeStruct data)
{
    // 只能读取,不能修改
    Console.WriteLine($"处理数据: {data.Value1}");
    // data.Value1 = 100;  // 编译错误:不能修改 in 参数
}

static void Main()
{
    LargeStruct data = new LargeStruct { Value1 = 42 };
    ProcessLargeStruct(in data);
}

可变参数 (params)

csharp
static int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int number in numbers)
    {
        total += number;
    }
    return total;
}

static void PrintMessages(string prefix, params string[] messages)
{
    foreach (string message in messages)
    {
        Console.WriteLine($"{prefix}: {message}");
    }
}

static void Main()
{
    // 可以传递任意数量的参数
    Console.WriteLine($"Sum() = {Sum()}");                    // 0
    Console.WriteLine($"Sum(1) = {Sum(1)}");                 // 1
    Console.WriteLine($"Sum(1, 2, 3) = {Sum(1, 2, 3)}");    // 6
    Console.WriteLine($"Sum(1, 2, 3, 4, 5) = {Sum(1, 2, 3, 4, 5)}");  // 15
    
    // 也可以传递数组
    int[] numbers = { 10, 20, 30 };
    Console.WriteLine($"Sum(array) = {Sum(numbers)}");       // 60
    
    // 带前缀的多个消息
    PrintMessages("INFO", "系统启动", "加载配置", "准备就绪");
}

方法重载

基本重载

csharp
class Calculator
{
    // 重载:不同参数数量
    public static int Add(int a, int b)
    {
        return a + b;
    }
    
    public static int Add(int a, int b, int c)
    {
        return a + b + c;
    }
    
    // 重载:不同参数类型
    public static double Add(double a, double b)
    {
        return a + b;
    }
    
    public static string Add(string a, string b)
    {
        return a + b;
    }
    
    // 重载:不同参数顺序
    public static void PrintInfo(string name, int age)
    {
        Console.WriteLine($"姓名: {name}, 年龄: {age}");
    }
    
    public static void PrintInfo(int age, string name)
    {
        Console.WriteLine($"年龄: {age}, 姓名: {name}");
    }
}

static void Main()
{
    Console.WriteLine(Calculator.Add(5, 3));           // int 版本
    Console.WriteLine(Calculator.Add(5, 3, 2));        // 三参数版本
    Console.WriteLine(Calculator.Add(5.5, 3.2));       // double 版本
    Console.WriteLine(Calculator.Add("Hello", "World")); // string 版本
    
    Calculator.PrintInfo("Alice", 25);                 // string, int 版本
    Calculator.PrintInfo(30, "Bob");                   // int, string 版本
}

重载解析规则

csharp
class OverloadDemo
{
    public static void Method(int x) => Console.WriteLine("int");
    public static void Method(double x) => Console.WriteLine("double");
    public static void Method(object x) => Console.WriteLine("object");
    
    public static void Method(int x, int y) => Console.WriteLine("int, int");
    public static void Method(double x, double y) => Console.WriteLine("double, double");
    
    static void Main()
    {
        Method(42);        // 精确匹配:int
        Method(3.14);      // 精确匹配:double
        Method("hello");   // 精确匹配:object
        
        Method(42, 3.14);  // 没有精确匹配,编译错误
        Method((double)42, 3.14);  // 显式转换:double, double
    }
}

可选参数和命名参数

可选参数

csharp
static void CreateUser(string name, int age = 18, string city = "北京", bool isActive = true)
{
    Console.WriteLine($"用户: {name}, 年龄: {age}, 城市: {city}, 活跃: {isActive}");
}

static void Main()
{
    // 使用所有默认值
    CreateUser("Alice");
    
    // 部分使用默认值
    CreateUser("Bob", 25);
    
    // 指定所有参数
    CreateUser("Charlie", 30, "上海", false);
}

命名参数

csharp
static void ConfigureServer(string host, int port, bool useSSL, int timeout)
{
    Console.WriteLine($"服务器: {host}:{port}, SSL: {useSSL}, 超时: {timeout}秒");
}

static void Main()
{
    // 位置参数
    ConfigureServer("localhost", 8080, true, 30);
    
    // 命名参数
    ConfigureServer(host: "example.com", port: 443, useSSL: true, timeout: 60);
    
    // 混合使用(命名参数必须在位置参数之后)
    ConfigureServer("api.example.com", port: 8443, useSSL: true, timeout: 120);
    
    // 改变参数顺序
    ConfigureServer(timeout: 90, host: "test.com", useSSL: false, port: 80);
}

局部函数 (C# 7.0+)

csharp
static void ProcessNumbers(int[] numbers)
{
    // 局部函数:只在当前方法内可见
    bool IsEven(int number)
    {
        return number % 2 == 0;
    }
    
    void PrintResult(string label, int count)
    {
        Console.WriteLine($"{label}: {count}");
    }
    
    // 使用局部函数
    int evenCount = 0;
    int oddCount = 0;
    
    foreach (int number in numbers)
    {
        if (IsEven(number))
            evenCount++;
        else
            oddCount++;
    }
    
    PrintResult("偶数个数", evenCount);
    PrintResult("奇数个数", oddCount);
}

static void Main()
{
    int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    ProcessNumbers(numbers);
}

递归方法

基本递归

csharp
// 计算阶乘
static long Factorial(int n)
{
    if (n <= 1)
        return 1;  // 基础情况
    else
        return n * Factorial(n - 1);  // 递归调用
}

// 计算斐波那契数列
static long Fibonacci(int n)
{
    if (n <= 1)
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2);
}

// 计算最大公约数(欧几里得算法)
static int GCD(int a, int b)
{
    if (b == 0)
        return a;
    else
        return GCD(b, a % b);
}

static void Main()
{
    Console.WriteLine($"5! = {Factorial(5)}");
    Console.WriteLine($"Fibonacci(10) = {Fibonacci(10)}");
    Console.WriteLine($"GCD(48, 18) = {GCD(48, 18)}");
}

尾递归优化

csharp
// 普通递归(可能导致栈溢出)
static long FactorialNormal(int n)
{
    if (n <= 1)
        return 1;
    return n * FactorialNormal(n - 1);
}

// 尾递归版本
static long FactorialTail(int n, long accumulator = 1)
{
    if (n <= 1)
        return accumulator;
    return FactorialTail(n - 1, n * accumulator);
}

// 迭代版本(推荐用于大数值)
static long FactorialIterative(int n)
{
    long result = 1;
    for (int i = 2; i <= n; i++)
    {
        result *= i;
    }
    return result;
}

实践示例

示例 1:数学工具类

csharp
using System;

class MathUtils
{
    // 判断是否为质数
    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 * i <= 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 Math.Abs(a);
    }
    
    // 计算两个数的最小公倍数
    public static int LCM(int a, int b)
    {
        return Math.Abs(a * b) / GCD(a, b);
    }
    
    // 计算数字的各位数字之和
    public static int SumOfDigits(int number)
    {
        int sum = 0;
        number = Math.Abs(number);
        
        while (number > 0)
        {
            sum += number % 10;
            number /= 10;
        }
        return sum;
    }
    
    // 判断是否为完全数
    public static bool IsPerfectNumber(int number)
    {
        if (number <= 1) return false;
        
        int sum = 1;  // 1 是所有数的因子
        for (int i = 2; i * i <= number; i++)
        {
            if (number % i == 0)
            {
                sum += i;
                if (i != number / i)  // 避免重复计算平方根
                    sum += number / i;
            }
        }
        return sum == number;
    }
    
    static void Main()
    {
        Console.WriteLine("=== 数学工具演示 ===");
        
        // 测试质数判断
        Console.WriteLine("\n质数测试:");
        int[] testNumbers = { 2, 3, 4, 17, 25, 29 };
        foreach (int num in testNumbers)
        {
            Console.WriteLine($"{num} 是质数: {IsPrime(num)}");
        }
        
        // 测试最大公约数和最小公倍数
        Console.WriteLine("\nGCD 和 LCM 测试:");
        Console.WriteLine($"GCD(48, 18) = {GCD(48, 18)}");
        Console.WriteLine($"LCM(48, 18) = {LCM(48, 18)}");
        
        // 测试数字各位之和
        Console.WriteLine("\n数字各位之和:");
        Console.WriteLine($"SumOfDigits(12345) = {SumOfDigits(12345)}");
        
        // 测试完全数
        Console.WriteLine("\n完全数测试:");
        for (int i = 1; i <= 30; i++)
        {
            if (IsPerfectNumber(i))
                Console.WriteLine($"{i} 是完全数");
        }
    }
}

示例 2:字符串处理工具

csharp
using System;
using System.Text;

class StringUtils
{
    // 反转字符串
    public static string Reverse(string input)
    {
        if (string.IsNullOrEmpty(input))
            return input;
            
        char[] chars = input.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
    
    // 判断是否为回文
    public static bool IsPalindrome(string input)
    {
        if (string.IsNullOrEmpty(input))
            return true;
            
        string cleaned = input.ToLower().Replace(" ", "");
        return cleaned == Reverse(cleaned);
    }
    
    // 统计单词数量
    public static int CountWords(string input)
    {
        if (string.IsNullOrWhiteSpace(input))
            return 0;
            
        string[] words = input.Trim().Split(new char[] { ' ', '\t', '\n', '\r' }, 
                                          StringSplitOptions.RemoveEmptyEntries);
        return words.Length;
    }
    
    // 首字母大写
    public static string Capitalize(string input)
    {
        if (string.IsNullOrEmpty(input))
            return input;
            
        return char.ToUpper(input[0]) + input.Substring(1).ToLower();
    }
    
    // 移除重复字符
    public static string RemoveDuplicates(string input)
    {
        if (string.IsNullOrEmpty(input))
            return input;
            
        StringBuilder result = new StringBuilder();
        foreach (char c in input)
        {
            if (result.ToString().IndexOf(c) == -1)
                result.Append(c);
        }
        return result.ToString();
    }
    
    static void Main()
    {
        Console.WriteLine("=== 字符串工具演示 ===");
        
        string testString = "Hello World";
        Console.WriteLine($"原字符串: '{testString}'");
        Console.WriteLine($"反转: '{Reverse(testString)}'");
        Console.WriteLine($"是否回文: {IsPalindrome(testString)}");
        Console.WriteLine($"单词数量: {CountWords(testString)}");
        Console.WriteLine($"首字母大写: '{Capitalize(testString)}'");
        Console.WriteLine($"移除重复: '{RemoveDuplicates("programming")}'");
        
        // 测试回文
        string[] palindromes = { "level", "A man a plan a canal Panama", "race car" };
        Console.WriteLine("\n回文测试:");
        foreach (string p in palindromes)
        {
            Console.WriteLine($"'{p}' 是回文: {IsPalindrome(p)}");
        }
    }
}

本章小结

本章详细介绍了 C# 中的方法和函数:

关键要点:

  • 方法定义:访问修饰符、返回类型、方法名、参数列表
  • 参数传递:值传递、引用传递(ref)、输出参数(out)、输入参数(in)
  • 方法重载:相同方法名,不同参数签名
  • 可选参数:提供默认值,简化方法调用
  • 局部函数:方法内部定义的辅助函数
  • 递归:方法调用自身解决问题

最佳实践:

  • 方法应该职责单一,功能明确
  • 使用有意义的方法名和参数名
  • 避免过长的参数列表
  • 合理使用方法重载
  • 注意递归的终止条件
  • 为公共方法添加 XML 文档注释

下一步: 在下一章中,我们将学习数组和集合,了解如何存储和操作多个数据项。

延伸阅读

本站内容仅供学习和研究使用。