专栏导航
- ← 上一篇:重复的力量 - 循环
- ← 第一篇:编程世界初探
- 专栏目录
什么是方法?
现实生活中的类比
想象你有一个工具箱:
┌─────────────────┐
│ 工具箱 │
├─────────────────┤
│ 🔧 锤子 │
│ 🔨 扳手 │
│ 🪚 锯子 │
│ 📏 尺子 │
└─────────────────┘
- 工具箱 = 程序
- 每个工具 = 方法(函数)
- 使用工具 = 调用方法
为什么要使用方法?
不使用方法(代码重复):
// 计算 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:无返回值的委托
语法: Action、Action<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 委托的使用
- ✅ 学会了将方法作为参数传递