现在位置: 首页 > C# 教程 > 正文

C# 方法(Methods)

方法(Method)是将一组相关语句组织在一起、用来执行特定任务的代码块。它是 C# 中实现代码复用逻辑封装的核心机制。每一个 C# 程序至少有一个 Main 方法作为程序入口。

使用方法需要两步:定义方法调用方法


1. 定义方法

C# 中定义方法的基本语法如下:

<访问修饰符> <返回类型> <方法名>(<参数列表>)
{
    方法体
}

方法由以下几个元素组成:

  • 访问修饰符(Access Modifier):控制方法的可见性,如 publicprivateprotectedinternal
  • 返回类型(Return Type):方法返回值的数据类型。如果方法不返回任何值,使用 void
  • 方法名(Method Name):方法的唯一标识符,采用 PascalCase 命名规范(如 FindMaxGetUserName)。
  • 参数列表(Parameter List):方法接收的输入,用圆括号括起来。参数是可选的,一个方法可以没有参数。
  • 方法体(Method Body):包含实现功能的代码,用花括号 {} 包围。

下图展示了一个方法的完整结构:

C# 方法结构解析 public int FindMax ( int num1, int num2 ) 访问修饰符 public / private 控制可见性 返回类型 int / void 返回值的数据类型 方法名 PascalCase 唯一标识符 参数列表 类型 + 名称 多个用逗号分隔 { 方法体 — 实现功能的代码 } 方法体(Method Body) 完整方法签名: public int FindMax(int a, int b) 修饰符 → 返回类型 → 方法名 → 参数列表

1.1 第一个方法示例

下面定义了一个 FindMax 方法,接收两个整数并返回其中较大的一个:

实例

using System;

namespace MethodDemo
{
    class NumberHelper
    {
        // 定义方法:接收两个 int,返回较大的那个
        public int FindMax(int num1, int num2)
        {
            if (num1 > num2)
                return num1;
            else
                return num2;
        }

        static void Main(string[] args)
        {
            // 创建对象并调用方法
            NumberHelper helper = new NumberHelper();
            int result = helper.FindMax(100, 200);

            Console.WriteLine($"最大值是:{result}");  // 最大值是:200
        }
    }
}

方法的调用流程:

方法调用流程 调用方 FindMax(100, 200) 实参 方法接收 num1=100, num2=200 执行方法体 比较并返回结果 返回 返回值 result = 200 1. 调用方法,传递实参 → 2. 形参接收值 → 3. 执行方法体 → 4. 返回结果给调用方

跨类调用:只要方法声明为 public,就可以从其他类中通过创建实例来调用。这是面向对象编程中代码复用的基础。


2. 参数传递方式

C# 提供三种向方法传递参数的方式,它们的核心区别在于是否共享内存

三种参数传递方式对比 按值传递(默认) 调用方 a = 100 复制 方法内 x = 100 各自独立的内存空间 修改 x 不影响 a swap(a, b) 后 a、b 不变 无需额外关键字 void Swap(int x, int y) 引用传递(ref) 调用方 a = 200 同一地址 方法内 ref x = 200 共享同一块内存 修改 x 会改变 a swap(ref a, ref b) 后交换成功 需要 ref 关键字 void Swap(ref int x, ref int y) 输出参数(out) 调用方 a = 5 ← 输出 方法内 out x = 5 方法必须为 out 参数赋值 调用前无需初始化 可返回多个值 需要 out 关键字 void Calc(out int x, out int y)
方式 关键字 行为 典型场景
按值传递 (无) 复制参数值,方法内修改不影响原变量 大多数方法调用(默认)
按引用传递 ref 传递变量的引用,方法内修改会改变原变量 需要修改外部变量(如交换值)
输出参数 out 类似 ref,但方法必须赋值,调用前无需初始化 方法需要返回多个值

2.1 按值传递(默认)

按值传递是默认方式。实参的值会被复制给形参,方法内对形参的修改不会影响原始变量:

实例

using System;

namespace ParameterDemo
{
    class Program
    {
        // 按值传递:x 和 y 是 a、b 的副本
        public void Swap(int x, int y)
        {
            int temp = x;
            x = y;
            y = temp;
            Console.WriteLine($"  方法内:x={x}, y={y}");  // 交换成功
        }

        static void Main(string[] args)
        {
            var p = new Program();
            int a = 100, b = 200;

            Console.WriteLine($"调用前:a={a}, b={b}");
            p.Swap(a, b);
            Console.WriteLine($"调用后:a={a}, b={b}");  // a、b 没有变化!
        }
    }
}
调用前:a=100, b=200
  方法内:x=200, y=100
调用后:a=100, b=200

2.2 按引用传递(ref)

使用 ref 关键字传递变量的引用(内存地址),方法内对形参的修改会直接影响原始变量。注意调用时也必须加上 ref

实例

using System;

namespace ParameterDemo
{
    class Program
    {
        // 引用传递:x 和 y 直接引用 a、b 的内存
        public void Swap(ref int x, ref int y)
        {
            int temp = x;
            x = y;
            y = temp;
        }

        static void Main(string[] args)
        {
            var p = new Program();
            int a = 100, b = 200;

            Console.WriteLine($"调用前:a={a}, b={b}");
            p.Swap(ref a, ref b);  // 调用时也必须加 ref
            Console.WriteLine($"调用后:a={a}, b={b}");  // 交换成功!
        }
    }
}
调用前:a=100, b=200
调用后:a=200, b=100

ref vs out 的区别:ref 要求变量在传递前必须已初始化out 则不要求,但方法内部必须为 out 参数赋值后才能使用。

2.3 输出参数(out)

out 参数让方法可以返回多个值。与 ref 不同的是,调用前变量无需初始化,但方法内部必须为其赋值:

实例

using System;

namespace ParameterDemo
{
    class Program
    {
        // out 参数:方法负责赋值
        public void GetValues(out int x, out int y)
        {
            Console.Write("请输入第一个值:");
            x = Convert.ToInt32(Console.ReadLine());

            Console.Write("请输入第二个值:");
            y = Convert.ToInt32(Console.ReadLine());
            // 注意:方法结束前必须为所有 out 参数赋值,否则编译错误
        }

        // 实用示例:TryParse 模式
        public bool TryDivide(int a, int b, out double result)
        {
            if (b == 0)
            {
                result = 0;
                return false;  // 除数为零
            }
            result = (double)a / b;
            return true;
        }

        static void Main(string[] args)
        {
            var p = new Program();

            // out 参数无需初始化
            int a, b;
            p.GetValues(out a, out b);
            Console.WriteLine($"a={a}, b={b}");

            // TryParse 模式
            if (p.TryDivide(10, 3, out double res))
                Console.WriteLine($"10 / 3 = {res:F2}");  // 3.33
        }
    }
}

从 C# 7.0 起,可以在调用时内联声明 out 变量,使代码更简洁:

// C# 7.0+ 内联声明
if (int.TryParse("123", out int number))
    Console.WriteLine($"解析成功:{number}");

3. 递归方法

递归(Recursion)是指方法调用自身。每个递归方法都需要两个要素:

  • 基准条件(Base Case):递归终止的条件,防止无限递归
  • 递归步骤(Recursive Step):将问题分解为更小的子问题

经典示例——计算阶乘 $$n! = n \times (n-1)!$$:

实例

using System;

namespace RecursionDemo
{
    class Program
    {
        // 递归计算阶乘
        public int Factorial(int n)
        {
            // 基准条件:0! = 1,1! = 1
            if (n <= 1)
                return 1;

            // 递归步骤:n! = n × (n-1)!
            return n * Factorial(n - 1);
        }

        static void Main(string[] args)
        {
            var p = new Program();
            Console.WriteLine($"6! = {p.Factorial(6)}");   // 720
            Console.WriteLine($"7! = {p.Factorial(7)}");   // 5040
            Console.WriteLine($"8! = {p.Factorial(8)}");   // 40320
        }
    }
}
6! = 720
7! = 5040
8! = 40320

递归调用的展开过程:

Factorial(4)
  = 4 × Factorial(3)
  = 4 × 3 × Factorial(2)
  = 4 × 3 × 2 × Factorial(1)
  = 4 × 3 × 2 × 1
  = 24

注意:递归深度过大时会导致 StackOverflowException。对于性能敏感的场景,考虑改用循环实现。


4. 方法的更多特性

4.1 默认参数与命名参数

C# 允许为参数设置默认值,调用时可以省略这些参数。配合命名参数,还可以跳过某些参数只指定后面的:

实例

using System;

namespace MethodFeatures
{
    class Program
    {
        // 默认参数:power 默认为 2
        static double Power(double baseNum, int power = 2)
        {
            double result = 1;
            for (int i = 0; i < power; i++)
                result *= baseNum;
            return result;
        }

        // 多个默认参数
        static void PrintInfo(string name, int age = 18, string city = "未知")
        {
            Console.WriteLine($"{name}, {age}岁, {city}");
        }

        static void Main(string[] args)
        {
            // 使用默认参数
            Console.WriteLine(Power(3));       // 9.0(使用默认 power=2)
            Console.WriteLine(Power(3, 3));    // 27.0

            // 命名参数:跳过 age,只指定 city
            PrintInfo("小明", city: "北京");
            // 输出:小明, 18岁, 北京

            // 命名参数可以不按顺序
            PrintInfo(city: "上海", name: "小红", age: 25);
            // 输出:小红, 25岁, 上海
        }
    }
}

4.2 表达式体方法

当方法体只有一条语句时,可以用 => 简化写法(C# 6.0+):

// 传统写法
public int Add(int a, int b)
{
    return a + b;
}

// 表达式体写法(等价)
public int Add(int a, int b) => a + b;

4.3 方法重载(Overloading)

同一个类中可以有多个同名方法,只要参数列表不同(参数类型、数量或顺序不同):

实例

using System;

namespace MethodFeatures
{
    class Calculator
    {
        // 重载 1:两个 int 相加
        public int Add(int a, int b) => a + b;

        // 重载 2:三个 int 相加
        public int Add(int a, int b, int c) => a + b + c;

        // 重载 3:两个 double 相加
        public double Add(double a, double b) => a + b;

        static void Main(string[] args)
        {
            var calc = new Calculator();
            Console.WriteLine(calc.Add(1, 2));         // 3(调用重载 1)
            Console.WriteLine(calc.Add(1, 2, 3));      // 6(调用重载 2)
            Console.WriteLine(calc.Add(1.5, 2.5));     // 4.0(调用重载 3)
        }
    }
}

小结

特性 语法 说明
定义方法 int Add(int a, int b) { ... } 指定返回类型、方法名和参数
无返回值 void DoSomething() { ... } void 表示不返回任何值
按值传递 void Foo(int x) 默认方式,修改形参不影响实参
引用传递 void Foo(ref int x) 共享内存,修改会反映到实参
输出参数 void Foo(out int x) 方法内必须赋值,可返回多个值
默认参数 void Foo(int x = 10) 调用时可省略,使用默认值
命名参数 Foo(city: "北京") 按名称指定参数,可不按顺序
表达式体 int Add(int a, int b) => a + b; 单行方法的简写(C# 6.0+)
方法重载 同名不同参数 编译器根据参数列表自动选择