第 5 课 · 约 12 分钟 · ← 第 4 课

方法:给菜谱起名字

方法是一段有名字的代码块,带输入(参数),通常有一个输出(返回值)。菜谱写一次,处处可用。

return:回答问题的方法

int Add(int a, int b)
{
    return a + b;          // 把这个值交还给调用方
}

Console.WriteLine(Add(3, 4));

签名从左往右读:int(返回什么)Add(叫什么)(int a, int b)(要什么)。调用处的 Add(3, 4) 就是值 7——它可以去任何值能去的地方。

void:做事的方法,不回答问题

void Greet(string name)
{
    Console.WriteLine("Hello, " + name + "!");
}

Greet("Ada");
Greet("Grace");

void 表示"什么都不返回"——方法做完事,控制权回到调用方。需要提前离场时,不带值的 return; 也可以。

多个 return,一个答案

方法返回它碰到的第一个 return——提前返回胜过深层嵌套:

string Describe(int score)
{
    if (score >= 90) return "excellent";
    if (score >= 70) return "good";
    return "keep practicing";
}

Console.WriteLine(Describe(95));
Console.WriteLine(Describe(75));
Console.WriteLine(Describe(30));

默认值:可选参数

void Greet(string name, string greeting = "Hello")
{
    Console.WriteLine(greeting + ", " + name + "!");
}

Greet("Ada");              // greeting 用默认值
Greet("Bob", "Hi");        // 也可以覆盖

out:一个方法回答两个问题

return 只能给你一个值。当一个方法既要产出值又要报告成败——int.TryParse 背后的现实模式——用 out:

bool TryDouble(int x, out int doubled)
{
    doubled = x * 2;             // 方法必须给 out 参数赋值
    return x < 100;              // ……并报告这个值能不能用
}

if (TryDouble(21, out int result))
{
    Console.WriteLine("doubled: " + result);
}

递归:调用自己的方法

int Fib(int n)
{
    if (n < 2) return n;          // 基线条件:终止自调用
    return Fib(n - 1) + Fib(n - 2);
}

Console.Write(Fib(10));

每个递归都要有基线条件(停止条件)——没有它,调用永不结束,你会得到 StackOverflowException。这里的引擎在约 900 层嵌套时拦停你,和真实运行时同一个思路。

参数出错的样子

参数个数给错,编译器在任何东西运行前就拦住你——你会看到 CS1501 / CS7036,参数数量不匹配是它们最常见的触发器。

现在就练