====== 第三章 方法 ======
===== 3.1 方法基础 =====
=== 什么是方法 ===
方法(Method)是执行特定任务的代码块,是组织和重用代码的基本单元。通过方法,我们可以将复杂的程序分解为可管理的小块。
=== 方法的基本结构 ===
[访问修饰符] [返回类型] 方法名([参数列表])
{
// 方法体
// 执行语句
[return 返回值;]
}
=== 方法定义示例 ===
// 无参数无返回值
void SayHello()
{
Console.WriteLine("Hello, World!");
}
// 有参数无返回值
void Greet(string name)
{
Console.WriteLine($"Hello, {name}!");
}
// 有参数有返回值
int Add(int a, int b)
{
return a + b;
}
// 无参数有返回值
string GetCurrentTime()
{
return DateTime.Now.ToString("HH:mm:ss");
}
===== 3.2 参数传递 =====
=== 值传递 ===
默认情况下,C#使用值传递。方法接收的是参数的副本,修改不影响原始值。
void Increment(int number)
{
number++; // 只修改副本
Console.WriteLine($"方法内部: {number}");
}
int value = 10;
Increment(value);
Console.WriteLine($"方法外部: {value}"); // 仍然是10
=== 引用传递(ref) ===
使用ref关键字,方法可以修改原始变量的值。
void IncrementByRef(ref int number)
{
number++; // 修改原始值
}
int value = 10;
IncrementByRef(ref value);
Console.WriteLine(value); // 输出11
=== 输出参数(out) ===
out参数用于从方法返回多个值,调用前不需要初始化。
void Calculate(int a, int b, out int sum, out int difference)
{
sum = a + b;
difference = a - b;
}
int s, d;
Calculate(10, 5, out s, out d);
Console.WriteLine($"和: {s}, 差: {d}");
// 丢弃不需要的输出
Calculate(10, 5, out int sum, out _);
=== in参数(C# 7.2+) ===
in参数提供只读的引用传递,提高大型结构体的传递效率。
void Display(in Point point)
{
Console.WriteLine($"X: {point.X}, Y: {point.Y}");
// point.X = 10; // 编译错误!不能修改in参数
}
struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
=== 参数数组(params) ===
params允许传递可变数量的参数。
int Sum(params int[] numbers)
{
int total = 0;
foreach (int num in numbers)
{
total += num;
}
return total;
}
// 多种调用方式
Console.WriteLine(Sum(1, 2, 3)); // 6
Console.WriteLine(Sum(1, 2, 3, 4, 5)); // 15
Console.WriteLine(Sum()); // 0
Console.WriteLine(Sum(new[] { 1, 2, 3 })); // 6
=== 命名参数和可选参数 ===
// 可选参数(带默认值)
void PrintInfo(string name, int age = 18, string city = "北京")
{
Console.WriteLine($"{name}, {age}岁, 来自{city}");
}
// 调用
PrintInfo("张三"); // 使用默认值
PrintInfo("李四", 25); // 指定年龄
PrintInfo("王五", city: "上海"); // 使用命名参数跳过前面的参数
PrintInfo(name: "赵六", age: 30, city: "广州"); // 全部使用命名参数
===== 3.3 返回值 =====
=== 单一返回值 ===
int Multiply(int a, int b)
{
return a * b;
}
string GetFullName(string firstName, string lastName)
{
return $"{lastName} {firstName}";
}
=== 元组返回多个值(C# 7+) ===
// 返回元组
(int sum, int difference) Calculate(int a, int b)
{
return (a + b, a - b);
}
// 调用
var result = Calculate(10, 5);
Console.WriteLine($"和: {result.sum}, 差: {result.difference}");
// 解构
(int s, int d) = Calculate(10, 5);
=== 引用返回值(C# 7+) ===
int[] numbers = { 1, 2, 3, 4, 5 };
ref int Find(int[] arr, int target)
{
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == target)
{
return ref arr[i];
}
}
throw new InvalidOperationException("未找到");
}
ref int found = ref Find(numbers, 3);
found = 30; // 修改数组中的值
Console.WriteLine(string.Join(", ", numbers)); // 1, 2, 30, 4, 5
===== 3.4 方法重载 =====
=== 重载规则 ===
方法重载允许在同一个类中定义多个同名方法,只要它们的参数列表不同。
class Calculator
{
// 重载Add方法
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
public string Add(string a, string b)
{
return a + b;
}
}
// 使用
var calc = new Calculator();
Console.WriteLine(calc.Add(1, 2)); // 3
Console.WriteLine(calc.Add(1.5, 2.5)); // 4.0
Console.WriteLine(calc.Add(1, 2, 3)); // 6
Console.WriteLine(calc.Add("Hello, ", "World")); // "Hello, World"
=== 重载决策 ===
class OverloadDemo
{
public void Method(int x) => Console.WriteLine("int");
public void Method(long x) => Console.WriteLine("long");
public void Method(double x) => Console.WriteLine("double");
public void Method(object x) => Console.WriteLine("object");
}
var demo = new OverloadDemo();
demo.Method(5); // int - 精确匹配
demo.Method(5L); // long
demo.Method(5.0); // double
demo.Method("text"); // object
===== 3.5 递归 =====
=== 递归基础 ===
递归是方法调用自身的编程技巧,必须包含终止条件。
// 阶乘计算
int Factorial(int n)
{
if (n <= 1) // 终止条件
return 1;
return n * Factorial(n - 1);
}
Console.WriteLine(Factorial(5)); // 120 (5! = 5*4*3*2*1)
=== 斐波那契数列 ===
int Fibonacci(int n)
{
if (n <= 1)
return n;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
// 输出前10个斐波那契数
for (int i = 0; i < 10; i++)
{
Console.Write(Fibonacci(i) + " ");
}
// 输出: 0 1 1 2 3 5 8 13 21 34
=== 递归 vs 迭代 ===
// 递归版本
int FactorialRecursive(int n)
{
if (n <= 1) return 1;
return n * FactorialRecursive(n - 1);
}
// 迭代版本(更高效)
int FactorialIterative(int n)
{
int result = 1;
for (int i = 2; i <= n; i++)
{
result *= i;
}
return result;
}
===== 3.6 局部函数(C# 7+) =====
=== 基本用法 ===
int CalculateFactorial(int n)
{
// 局部函数
int Factorial(int x)
{
if (x <= 1) return 1;
return x * Factorial(x - 1);
}
if (n < 0)
throw new ArgumentException("n必须非负");
return Factorial(n);
}
=== 捕获变量 ===
void OuterMethod(int factor)
{
// 局部函数可以访问外部方法的变量
int Multiply(int x) => x * factor;
Console.WriteLine(Multiply(5)); // 5 * factor
Console.WriteLine(Multiply(10)); // 10 * factor
}
===== 3.7 匿名方法与Lambda表达式 =====
=== 匿名方法(C# 2.0) ===
// 已较少使用,主要用于兼容性
delegate void MyDelegate(string message);
MyDelegate del = delegate(string msg)
{
Console.WriteLine(msg);
};
del("Hello from anonymous method");
=== Lambda表达式(C# 3.0+) ===
// 表达式Lambda
Func square = x => x * x;
Console.WriteLine(square(5)); // 25
// 语句Lambda
Action greet = name =>
{
Console.WriteLine($"Hello, {name}!");
Console.WriteLine("Welcome!");
};
greet("Alice");
// 多参数Lambda
Func add = (a, b) => a + b;
// 无参数Lambda
Action sayHello = () => Console.WriteLine("Hello!");
===== 3.8 扩展方法 =====
=== 定义扩展方法 ===
// 必须定义在静态类中
public static class StringExtensions
{
// 使用this关键字
public static bool IsNullOrEmpty(this string str)
{
return string.IsNullOrEmpty(str);
}
public static string Reverse(this string str)
{
char[] chars = str.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
public static int WordCount(this string str)
{
return str.Split(new[] { ' ', '\t', '\n' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
// 使用
string text = "Hello World";
Console.WriteLine(text.Reverse()); // dlroW olleH
Console.WriteLine(text.WordCount()); // 2
===== 3.9 方法实战 =====
=== 数值计算工具类 ===
public static class MathUtils
{
// 计算数组平均值
public static double Average(params double[] numbers)
{
if (numbers.Length == 0)
throw new ArgumentException("数组不能为空");
return numbers.Average();
}
// 判断素数
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 <= Math.Sqrt(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 a;
}
// 计算最小公倍数
public static int LCM(int a, int b)
{
return Math.Abs(a * b) / GCD(a, b);
}
}
=== 字符串处理工具类 ===
public static class StringUtils
{
// 截断字符串
public static string Truncate(this string str, int maxLength,
string suffix = "...")
{
if (string.IsNullOrEmpty(str) || str.Length <= maxLength)
return str;
return str.Substring(0, maxLength - suffix.Length) + suffix;
}
// 驼峰命名转换
public static string ToCamelCase(this string str)
{
if (string.IsNullOrEmpty(str)) return str;
var words = str.Split(new[] { ' ', '_', '-' },
StringSplitOptions.RemoveEmptyEntries);
return words[0].ToLower() +
string.Concat(words.Skip(1).Select(w =>
char.ToUpper(w[0]) + w.Substring(1).ToLower()));
}
// 安全的转换为整数
public static int? ToIntOrNull(this string str)
{
return int.TryParse(str, out int result) ? result : (int?)null;
}
}
===== 练习题 =====
=== 基础练习 ===
1. **基础方法**:编写一个方法IsEven,判断一个整数是否为偶数,返回布尔值。
2. **参数传递**:解释以下代码的输出结果,并说明原因:
void Swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 5, y = 10;
Swap(x, y);
Console.WriteLine($"x={x}, y={y}");
然后使用ref关键字修改方法,使交换真正生效。
3. **方法重载**:为Calculate类添加重载方法,支持加法、减法、乘法和除法运算,分别处理int和double类型。
=== 进阶练习 ===
4. **递归实现**:使用递归实现以下功能:
- 计算1+2+3+...+n的和
- 计算x的n次幂
- 反转字符串
5. **多返回值**:编写一个方法AnalyzeNumber,接收一个整数,返回:
- 是否为偶数
- 是否为素数
- 各位数字之和
使用元组返回多个值。
6. **扩展方法**:为DateTime类型添加扩展方法:
- IsWeekend() - 判断是否为周末
- StartOfWeek() - 获取本周开始日期
- ToFriendlyString() - 返回友好的时间描述(如"今天"、"昨天"、"3天前"等)
=== 挑战练习 ===
7. **表达式计算器**:实现一个简单的方法,可以解析并计算形如"1+2*3"的简单数学表达式(支持+、-、*、/和括号)。
8. **排列组合**:编写方法计算n个元素的排列数和组合数,使用递归和迭代两种实现方式。
9. **方法链式调用**:设计一个Fluent API的StringBuilder类,支持链式调用,例如:
var result = new FluentBuilder()
.Append("Hello")
.AppendLine()
.Append("World")
.Replace("World", "C#")
.ToString();
===== 本章小结 =====
本章学习了C#方法的核心概念:
- 方法的定义和调用
- 各种参数传递方式(值、ref、out、in、params)
- 可选参数和命名参数
- 单一返回值和多元组返回值
- 方法重载的规则
- 递归的概念和应用
- 局部函数的使用
- Lambda表达式
- 扩展方法
掌握这些方法技巧后,你可以编写出结构清晰、可重用性高的代码。