组织代码 - 方法/函数

18 阅读8分钟

专栏导航

什么是方法?

现实生活中的类比

想象你有一个工具箱:

┌─────────────────┐
│  工具箱         │
├─────────────────┤
│ 🔧 锤子         │
│ 🔨 扳手         │
│ 🪚 锯子         │
│ 📏 尺子         │
└─────────────────┘
  • 工具箱 = 程序
  • 每个工具 = 方法(函数)
  • 使用工具 = 调用方法

为什么要使用方法?

不使用方法(代码重复):

// 计算 5 的圆面积
double pi = 3.14;
double r1 = 5;
double area1 = pi * r1 * r1;
Console.WriteLine($"半径为 {r1} 的面积:{area1}");

// 计算 10 的圆面积
double pi2 = 3.14;
double r2 = 10;
double area2 = pi2 * r2 * r2;
Console.WriteLine($"半径为 {r2} 的面积:{area2}");

// 计算 15 的圆面积
double pi3 = 3.14;
double r3 = 15;
double area3 = pi3 * r3 * r3;
Console.WriteLine($"半径为 {r3} 的面积:{area3}");

使用方法(简洁高效):

// 定义方法
double CalculateCircleArea(double radius)
{
    double pi = 3.14;
    return pi * radius * radius;
}

// 调用方法
Console.WriteLine($"面积:{CalculateCircleArea(5)}");
Console.WriteLine($"面积:{CalculateCircleArea(10)}");
Console.WriteLine($"面积:{CalculateCircleArea(15)}");

方法的好处

避免代码重复:一次定义,多次调用
提高可读性:代码结构清晰,易于理解
便于维护:修改一处,所有调用处都更新
提高复用性:方法可以在不同地方使用

定义方法

基本语法

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

示例:无返回值的方法

// 输出欢迎信息
void SayHello()
{
    Console.WriteLine("欢迎来到编程世界!");
}

// 调用方法
SayHello();

示例:带返回值的方法

// 计算两个数的和
int Add(int a, int b)
{
    int sum = a + b;
    return sum;
}

// 调用方法
int result = Add(3, 5);
Console.WriteLine(result);  // 输出:8

示例:带多个参数的方法

// 计算矩形面积
double CalculateRectangleArea(double width, double height)
{
    return width * height;
}

// 调用方法
double area = CalculateRectangleArea(5, 3);
Console.WriteLine($"面积:{area}");  // 输出:面积:15

方法的组成部分

1. 返回值类型

方法可以返回一个值,也可以不返回值。

// 返回整数
int Add(int a, int b)
{
    return a + b;
}

// 返回布尔值
bool IsEven(int number)
{
    return number % 2 == 0;
}

// 不返回值
void PrintMessage(string message)
{
    Console.WriteLine(message);
}

2. 参数

参数是方法接收的输入值。

// 单个参数
double CalculateSquare(double side)
{
    return side * side;
}

// 多个参数
double CalculateAverage(int a, int b, int c)
{
    return (a + b + c) / 3.0;
}

// 无参数
string GetCurrentTime()
{
    return DateTime.Now.ToString();
}

3. 方法体

方法体是方法实际执行的代码块。

void PrintGrades(int score)
{
    // 方法体
    if (score >= 90)
    {
        Console.WriteLine("优秀");
    }
    else if (score >= 60)
    {
        Console.WriteLine("及格");
    }
    else
    {
        Console.WriteLine("不及格");
    }
}

调用方法

语法

方法名(参数列表);

示例

// 定义方法
void Greet(string name)
{
    Console.WriteLine($"你好,{name}!");
}

// 调用方法
Greet("张三");  // 输出:你好,张三!
Greet("李四");  // 输出:你好,李四!

调用带返回值的方法

int Add(int a, int b)
{
    return a + b;
}

// 方式一:接收返回值
int result = Add(3, 5);
Console.WriteLine(result);

// 方式二:直接使用返回值
Console.WriteLine(Add(10, 20));

实践:将计算圆面积封装成方法

using System;

namespace Week6Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 圆面积计算器 =====\n");

            // 调用方法计算不同半径的面积
            double area1 = CalculateCircleArea(5);
            Console.WriteLine($"半径 5 的面积:{area1:F2}");

            double area2 = CalculateCircleArea(10);
            Console.WriteLine($"半径 10 的面积:{area2:F2}");

            double area3 = CalculateCircleArea(15);
            Console.WriteLine($"半径 15 的面积:{area3:F2}");

            // 计算用户输入的半径
            Console.Write("\n请输入圆的半径:");
            double inputRadius = double.Parse(Console.ReadLine());
            double userArea = CalculateCircleArea(inputRadius);
            Console.WriteLine($"半径 {inputRadius} 的面积:{userArea:F2}");
        }

        // 计算圆面积的方法
        static double CalculateCircleArea(double radius)
        {
            double pi = 3.1415926;
            double area = pi * radius * radius;
            return area;
        }
    }
}

输出:

===== 圆面积计算器 =====

半径 5 的面积:78.54
半径 10 的面积:314.16
半径 15 的面积:706.86

请输入圆的半径:8
半径 8 的面积:201.06

代码说明

  • static:静态方法,不需要创建对象就能调用
  • double:返回值类型
  • CalculateCircleArea:方法名(首字母大写,使用帕斯卡命名法)
  • double radius:参数
  • return area:返回计算结果

实践:判断奇偶数

using System;

namespace Week6Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 奇偶数判断 =====\n");

            // 测试多个数字
            int[] numbers = { 1, 2, 3, 4, 5, 10, 15, 20 };

            foreach (int num in numbers)
            {
                bool isEven = IsEven(num);
                string type = isEven ? "偶数" : "奇数";
                Console.WriteLine($"{num}{type}");
            }
        }

        // 判断是否为偶数
        static bool IsEven(int number)
        {
            return number % 2 == 0;
        }
    }
}

输出:

===== 奇偶数判断 =====

1 是 奇数
2 是 偶数
3 是 奇数
4 是 偶数
5 是 奇数
10 是 偶数
15 是 奇数
20 是 偶数

实践:计算器

using System;

namespace Week6Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("===== 简易计算器 =====");

            while (true)
            {
                Console.Write("\n请输入第一个数字(输入 q 退出):");
                string input1 = Console.ReadLine();
                if (input1.ToLower() == "q") break;

                Console.Write("请输入运算符(+、-、*、/):");
                string op = Console.ReadLine();

                Console.Write("请输入第二个数字:");
                string input2 = Console.ReadLine();

                double num1 = double.Parse(input1);
                double num2 = double.Parse(input2);
                double result = 0;

                // 调用不同的方法
                switch (op)
                {
                    case "+":
                        result = Add(num1, num2);
                        break;
                    case "-":
                        result = Subtract(num1, num2);
                        break;
                    case "*":
                        result = Multiply(num1, num2);
                        break;
                    case "/":
                        result = Divide(num1, num2);
                        break;
                    default:
                        Console.WriteLine("无效的运算符!");
                        continue;
                }

                Console.WriteLine($"结果:{num1} {op} {num2} = {result}");
            }

            Console.WriteLine("再见!");
        }

        // 加法
        static double Add(double a, double b)
        {
            return a + b;
        }

        // 减法
        static double Subtract(double a, double b)
        {
            return a - b;
        }

        // 乘法
        static double Multiply(double a, double b)
        {
            return a * b;
        }

        // 除法
        static double Divide(double a, double b)
        {
            if (b == 0)
            {
                Console.WriteLine("错误:除数不能为零!");
                return 0;
            }
            return a / b;
        }
    }
}

方法重载

同一个类中可以有多个同名方法,只要参数列表不同即可。

示例

// 计算两个数的和
int Add(int a, int b)
{
    return a + b;
}

// 计算三个数的和(方法重载)
int Add(int a, int b, int c)
{
    return a + b + c;
}

// 计算两个小数的和(方法重载)
double Add(double a, double b)
{
    return a + b;
}

// 调用
Console.WriteLine(Add(1, 2));      // 调用 int Add(int, int)
Console.WriteLine(Add(1, 2, 3));  // 调用 int Add(int, int, int)
Console.WriteLine(Add(1.5, 2.5)); // 调用 double Add(double, double)

实践:输出信息

// 无参数
void PrintInfo()
{
    Console.WriteLine("欢迎使用!");
}

// 一个参数
void PrintInfo(string name)
{
    Console.WriteLine($"欢迎,{name}!");
}

// 两个参数
void PrintInfo(string name, int age)
{
    Console.WriteLine($"欢迎,{name}!你今年 {age} 岁。");
}

// 调用
PrintInfo();                  // 输出:欢迎使用!
PrintInfo("张三");            // 输出:欢迎,张三!
PrintInfo("李四", 25);        // 输出:欢迎,李四!你今年 25 岁。

方法命名规范

命名规则

推荐:

  • 使用动词或动词短语
  • 帕斯卡命名法(首字母大写)
  • 语义清晰,见名知意
CalculateArea()      // 计算面积
GetUserInfo()        // 获取用户信息
SaveData()           // 保存数据
IsValidEmail()       // 验证邮箱是否有效

不推荐:

area()               // 太简单
doSomething()         // 含义不清
calculate()           // 不够具体

常见方法命名前缀/后缀

前缀/后缀含义示例
Get获取数据GetName(), GetAge()
Set设置数据SetName(), SetAge()
Is返回布尔值IsEven(), IsValid()
Has判断是否有HasPermission(), HasItems()
Calculate计算CalculateArea(), CalculateSum()
Convert转换ToString(), ToInt()
Add添加AddItem(), AddUser()
Remove删除RemoveItem(), RemoveUser()

参数传递

值传递(默认)

参数的值被复制到方法内部,不会影响原始变量。

void ChangeValue(int x)
{
    x = 100;
    Console.WriteLine($"方法内 x = {x}");  // 100
}

int num = 10;
ChangeValue(num);
Console.WriteLine($"方法外 num = {num}");  // 10(不受影响)

引用传递(ref)

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

void ChangeValue(ref int x)
{
    x = 100;
    Console.WriteLine($"方法内 x = {x}");  // 100
}

int num = 10;
ChangeValue(ref num);  // 注意调用时也要加 ref
Console.WriteLine($"方法外 num = {num}");  // 100(已被修改)

输出参数(out)

用于返回多个值。

void GetMinMax(int[] numbers, out int min, out int max)
{
    min = numbers[0];
    max = numbers[0];
    foreach (int num in numbers)
    {
        if (num < min) min = num;
        if (num > max) max = num;
    }
}

int[] arr = { 3, 1, 4, 1, 5, 9, 2, 6 };
int min, max;
GetMinMax(arr, out min, out max);
Console.WriteLine($"最小值:{min},最大值:{max}");
// 输出:最小值:1,最大值:9

实践:学生成绩管理系统

using System;
using System.Collections.Generic;

namespace Week6Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string>();
            List<double> scores = new List<double>();

            while (true)
            {
                Console.WriteLine("\n===== 学生成绩管理系统 =====");
                Console.WriteLine("1. 添加学生");
                Console.WriteLine("2. 查看所有学生");
                Console.WriteLine("3. 计算平均分");
                Console.WriteLine("4. 查找最高分");
                Console.WriteLine("5. 退出");
                Console.Write("请选择:");

                string choice = Console.ReadLine();

                switch (choice)
                {
                    case "1":
                        AddStudent(names, scores);
                        break;
                    case "2":
                        DisplayStudents(names, scores);
                        break;
                    case "3":
                        CalculateAverage(scores);
                        break;
                    case "4":
                        FindHighestScore(names, scores);
                        break;
                    case "5":
                        Console.WriteLine("再见!");
                        return;
                    default:
                        Console.WriteLine("无效选择!");
                        break;
                }
            }
        }

        // 添加学生
        static void AddStudent(List<string> names, List<double> scores)
        {
            Console.Write("请输入学生姓名:");
            string name = Console.ReadLine();

            Console.Write("请输入学生成绩:");
            double score = double.Parse(Console.ReadLine());

            names.Add(name);
            scores.Add(score);

            Console.WriteLine($"已添加学生:{name},成绩:{score}");
        }

        // 显示所有学生
        static void DisplayStudents(List<string> names, List<double> scores)
        {
            if (names.Count == 0)
            {
                Console.WriteLine("暂无学生数据!");
                return;
            }

            Console.WriteLine("\n===== 学生列表 =====");
            for (int i = 0; i < names.Count; i++)
            {
                Console.WriteLine($"{i + 1}. {names[i]} - {scores[i]}");
            }
        }

        // 计算平均分
        static void CalculateAverage(List<double> scores)
        {
            if (scores.Count == 0)
            {
                Console.WriteLine("暂无学生数据!");
                return;
            }

            double sum = 0;
            foreach (double score in scores)
            {
                sum += score;
            }
            double average = sum / scores.Count;
            Console.WriteLine($"平均分:{average:F2}");
        }

        // 查找最高分
        static void FindHighestScore(List<string> names, List<double> scores)
        {
            if (scores.Count == 0)
            {
                Console.WriteLine("暂无学生数据!");
                return;
            }

            double maxScore = scores[0];
            string topStudent = names[0];

            for (int i = 1; i < scores.Count; i++)
            {
                if (scores[i] > maxScore)
                {
                    maxScore = scores[i];
                    topStudent = names[i];
                }
            }

            Console.WriteLine($"最高分:{maxScore},学生:{topStudent}");
        }
    }
}

方法作为参数:Func 和 Action

什么是委托?

委托(Delegate) 可以理解为"方法的容器"或"方法的引用"。就像变量可以存储数字一样,委托可以存储方法。

为什么需要委托?

想象你有一个计算器,需要支持不同的运算方式:

// 传统方式:每种运算写一个方法
void ExecuteAdd(int a, int b)
{
    Console.WriteLine(a + b);
}

void ExecuteMultiply(int a, int b)
{
    Console.WriteLine(a * b);
}

这种方式重复代码很多!如果用委托,可以让方法作为参数传递:

// 一个通用方法,接收另一个方法作为参数
void ExecuteOperation(int a, int b, Func<int, int, int> operation)
{
    Console.WriteLine(operation(a, b));
}

Action 和 Func 的区别

类型说明语法格式示例
Action无返回值的方法Action<T1, T2, ...>Action<int, int> 表示接收两个 int 参数,无返回值
Func有返回值的方法Func<T1, T2, ..., TResult>Func<int, int, int> 表示接收两个 int 参数,返回 int

Action:无返回值的委托

语法: ActionAction<T>Action<T1, T2> ...

// 1. 无参数的 Action
Action sayHello = () => Console.WriteLine("你好!");
sayHello();  // 输出:你好!

// 2. 一个参数的 Action
Action<string> greet = (name) => Console.WriteLine($"你好,{name}!");
greet("张三");  // 输出:你好,张三!

// 3. 两个参数的 Action
Action<int, int> printSum = (a, b) => Console.WriteLine($"和:{a + b}");
printSum(5, 3);  // 输出:和:8

使用场景:传递一个不返回结果的操作

void ProcessItems(int[] items, Action<int> processor)
{
    foreach (int item in items)
    {
        processor(item);  // 调用传入的方法
    }
}

int[] numbers = { 1, 2, 3, 4, 5 };

// 方式一:打印每个数字
ProcessItems(numbers, num => Console.WriteLine($"数字:{num}"));

// 方式二:打印每个数字的平方
ProcessItems(numbers, num => Console.WriteLine($"平方:{num * num}"));

// 方式三:判断奇偶
ProcessItems(numbers, num =>
{
    string type = num % 2 == 0 ? "偶数" : "奇数";
    Console.WriteLine($"{num}{type}");
});

输出:

数字:1
数字:2
数字:3
数字:4
数字:5

平方:1
平方:4
平方:9
平方:16
平方:25

1 是 奇数
2 是 偶数
3 是 奇数
4 是 偶数
5 是 奇数

Func:有返回值的委托

语法: Func<TResult>Func<T, TResult>Func<T1, T2, TResult> ...

注意:最后一个泛型参数永远是返回值类型!

// 1. 无参数,返回 int
Func<int> getRandom = () => new Random().Next(1, 100);
Console.WriteLine(getRandom());  // 输出:随机数

// 2. 一个参数,返回结果
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));  // 输出:25

// 3. 两个参数,返回结果
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 5));  // 输出:8

// 4. 多个参数
Func<int, int, int, int> sum = (a, b, c) => a + b + c;
Console.WriteLine(sum(1, 2, 3));  // 输出:6

使用场景:传递一个会返回结果的计算

double Calculate(double a, double b, Func<double, double, double> operation)
{
    return operation(a, b);
}

double num1 = 10, num2 = 5;

// 使用不同的运算
double sum = Calculate(num1, num2, (x, y) => x + y);
Console.WriteLine($"和:{sum}");

double product = Calculate(num1, num2, (x, y) => x * y);
Console.WriteLine($"积:{product}");

double power = Calculate(num1, num2, (x, y) => Math.Pow(x, y));
Console.WriteLine($"幂:{power}");

实践:使用 Func 重构圆面积计算

不使用 Func:

static void Main(string[] args)
{
    double r = 5;
    double area = CalculateCircleArea(r);
    Console.WriteLine($"半径 {r} 的面积:{area}");
}

static double CalculateCircleArea(double radius)
{
    return 3.1415926 * radius * radius;
}

使用 Func 更灵活:

static void Main(string[] args)
{
    double r = 5;

    // 定义圆面积计算方法
    Func<double, double> calculateArea = radius => 3.1415926 * radius * radius;

    // 调用
    double area = calculateArea(r);
    Console.WriteLine($"半径 {r} 的面积:{area:F2}");

    // 也可以传递给其他方法
    PrintResult(r, calculateArea);
}

// 接收 Func 作为参数的方法
static void PrintResult(double radius, Func<double, double> calculator)
{
    double result = calculator(radius);
    Console.WriteLine($"计算结果:{result:F2}");
}

实践:学生成绩处理(综合应用 Action 和 Func)

using System;
using System.Collections.Generic;

namespace Week6Practice
{
    class Program
    {
        static void Main(string[] args)
        {
            List<double> scores = new List<double> { 85, 92, 78, 90, 88 };

            Console.WriteLine("===== 成绩处理系统 =====\n");

            // 1. 使用 Action 处理每个成绩
            Console.WriteLine("1. 打印所有成绩:");
            ProcessScores(scores, score => Console.WriteLine($"  成绩:{score}"));

            // 2. 使用 Action 判断等级
            Console.WriteLine("\n2. 判断等级:");
            ProcessScores(scores, score =>
            {
                string grade = score >= 90 ? "优秀" : score >= 60 ? "及格" : "不及格";
                Console.WriteLine($"  {score} - {grade}");
            });

            // 3. 使用 Func 计算统计信息
            Console.WriteLine("\n3. 统计信息:");
            double avg = CalculateStats(scores, list =>
            {
                double sum = 0;
                foreach (double s in list) sum += s;
                return sum / list.Count;
            });
            Console.WriteLine($"  平均分:{avg:F2}");

            double max = CalculateStats(scores, list =>
            {
                double maximum = list[0];
                foreach (double s in list)
                    if (s > maximum) maximum = s;
                return maximum;
            });
            Console.WriteLine($"  最高分:{max:F2}");

            double min = CalculateStats(scores, list =>
            {
                double minimum = list[0];
                foreach (double s in list)
                    if (s < minimum) minimum = s;
                return minimum;
            });
            Console.WriteLine($"  最低分:{min:F2}");

            // 4. 筛选及格成绩
            Console.WriteLine("\n4. 及格成绩:");
            List<double> passed = FilterScores(scores, score => score >= 60);
            foreach (double score in passed)
                Console.WriteLine($"  {score}");
        }

        // 处理每个成绩(使用 Action)
        static void ProcessScores(List<double> scores, Action<double> processor)
        {
            foreach (double score in scores)
            {
                processor(score);
            }
        }

        // 计算统计信息(使用 Func)
        static double CalculateStats(List<double> scores, Func<List<double>, double> calculator)
        {
            return calculator(scores);
        }

        // 筛选成绩(使用 Func)
        static List<double> FilterScores(List<double> scores, Func<double, bool> predicate)
        {
            List<double> result = new List<double>();
            foreach (double score in scores)
            {
                if (predicate(score))
                    result.Add(score);
            }
            return result;
        }
    }
}

输出:

===== 成绩处理系统 =====

1. 打印所有成绩:
  成绩:85
  成绩:92
  成绩:78
  成绩:90
  成绩:88

2. 判断等级:
  85 - 及格
  92 - 优秀
  78 - 及格
  90 - 优秀
  88 - 及格

3. 统计信息:
  平均分:86.60
  最高分:92.00
  最低分:78.00

4. 及格成绩:
  85
  92
  78
  90
  88

常用 Func 类型速查表

类型含义
Func<TResult>无参数,返回 TResult
Func<T, TResult>1个参数 T,返回 TResult
Func<T1, T2, TResult>2个参数,返回 TResult
Func<T1, T2, T3, TResult>3个参数,返回 TResult
Func<T1, T2, T3, T4, TResult>4个参数,返回 TResult

常用 Action 类型速查表

类型含义
Action无参数,无返回值
Action<T>1个参数 T
Action<T1, T2>2个参数
Action<T1, T2, T3>3个参数
Action<T1, T2, T3, T4>4个参数

什么时候使用 Action 和 Func?

适合使用的场景:

  • 需要将方法作为参数传递给其他方法
  • 需要根据不同情况执行不同的逻辑
  • 需要计算一个值,但计算方式可以变化
  • 使用 LINQ(第17章会学到)

Action 适用场景:

  • 只需要执行某个操作,不需要返回结果
  • 遍历集合并对每个元素做处理

Func 适用场景:

  • 需要计算并返回结果
  • 需要判断某个条件(返回 bool)
  • 需要转换数据

小贴士

💡 Lambda 表达式简化写法:

// 完整写法
Func<int, int> square = (x) => { return x * x; };

// 简化写法(单语句可省略花括号和 return)
Func<int, int> square = x => x * x;

💡 Action 和 Func 本质是委托类型:

// 这两种写法等价
Action<int> print1 = x => Console.WriteLine(x);
Action<int> print2 = new Action<int>(x => Console.WriteLine(x));

本章总结

  • ✅ 理解了方法的概念和好处
  • ✅ 学会了定义和调用方法
  • ✅ 掌握了参数和返回值的使用
  • ✅ 了解了方法重载
  • ✅ 学会了将功能封装成方法
  • ✅ 掌握了 Action 和 Func 委托的使用
  • ✅ 学会了将方法作为参数传递