目录
一.迭代
迭代器示例:
关键点:
优势:
二.递归
递归示例:
关键点:
优势:
注意:
三.回调
回调示例:
关键点:
优势:
应用场景:
4.三种模式的特点对比:
迭代:
递归:
回调:
一.迭代
在C#中迭代通常指重复执行一系列指令
在C#中,迭代器是一种特殊的结构,允许我们自定义遍历集合的方式,主要使用yield return
关键字
yield return
关键字的主要优点包括:
- 简化了迭代器的实现:不需要手动维护状态或创建临时集合
- 提高了代码的可读性和可维护性:使用
yield return
的方法显得更加直观- 延迟执行:元素在需要时才生成,适合处理大量数据或计算密集型操作
除了
yield return
,还有一个相关的关键字yield break
,用于终止迭代器并退出迭代
迭代器示例:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("迭代器示例:\n");
// 1. 使用迭代器方法
Console.WriteLine("1. 斐波那契数列(前10个数):");
foreach (var num in Fibonacci(10))
{
Console.Write($"{num} ");
}
Console.WriteLine("\n");
// 2. 自定义集合迭代
var customCollection = new CustomCollection(5);
Console.WriteLine("2. 自定义集合迭代:");
foreach (var item in customCollection)
{
Console.Write($"{item} ");
}
Console.WriteLine("\n");
// 3. yield return 示例
Console.WriteLine("3. 生成偶数序列(0-10):");
foreach (var even in GetEvenNumbers(10))
{
Console.Write($"{even} ");
}
Console.WriteLine("\n");
// 4. 带条件的迭代器
Console.WriteLine("4. 按条件过滤的数字(1-20中能被3整除的数):");
foreach (var num in GetNumbersDivisibleBy3(20))
{
Console.Write($"{num} ");
}
Console.WriteLine("\n");
Console.ReadKey();
}
// 斐波那契数列迭代器
static IEnumerable<int> Fibonacci(int count)
{
int current = 0, next = 1;
for (int i = 0; i < count; i++)
{
yield return current;
int temp = current + next;
current = next;
next = temp;
}
}
// 生成偶数的迭代器
static IEnumerable<int> GetEvenNumbers(int max)
{
for (int i = 0; i <= max; i++)
{
if (i % 2 == 0)
yield return i;
}
}
// 能被3整除的数的迭代器
static IEnumerable<int> GetNumbersDivisibleBy3(int max)
{
for (int i = 1; i <= max; i++)
{
if (i % 3 == 0)
yield return i;
}
}
}
// 自定义可迭代集合
class CustomCollection : IEnumerable<int>
{
private int[] array;
public CustomCollection(int size)
{
array = new int[size];
for (int i = 0; i < size; i++)
{
array[i] = i * i; // 存储数字的平方
}
}
public IEnumerator<int> GetEnumerator()
{
for (int i = 0; i < array.Length; i++)
{
yield return array[i];
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
关键点:
-
循环结构: 在C#中,迭代通常通过
for
、while
、do...while
、foreach
等循环结构实现 -
用途: 用于在数量已知或者条件可判定的情况下重复执行某段代码,例如遍历数组、执行固定次数的计算等
-
控制条件: 迭代需要有明确的开始条件、结束条件,以及每次迭代时的状态更新,避免出现无限循环
优势:
- 可控性强: 通过明确的控制条件,可以精确地控制迭代次数
- 效率高: 迭代通常比递归占用更少的内存,因为不涉及函数调用的堆栈开销
二.递归
递归是指函数在其定义中直接或间接调用自身的编程技巧
递归通常用于解决可以分解为相同子问题的问题,如阶乘,斐波那契数列等
递归示例:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("递归示例演示:\n");
// 1. 阶乘计算
int n = 5;
Console.WriteLine($"1. {n}的阶乘是: {CalculateFactorial(n)}");
// 2. 斐波那契数列
int position = 8;
Console.WriteLine($"\n2. 斐波那契数列第{position}个数是: {Fibonacci(position)}");
// 3. 数组求和
int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine($"\n3. 数组求和结果: {ArraySum(numbers, numbers.Length - 1)}");
// 4. 字符串反转
string text = "Hello, World!";
Console.WriteLine($"\n4. 字符串 \"{text}\" 反转后: {ReverseString(text)}");
// 5. 最大公约数
int a = 48, b = 36;
Console.WriteLine($"\n5. {a}和{b}的最大公约数是: {GCD(a, b)}");
// 6. 汉诺塔问题
Console.WriteLine("\n6. 汉诺塔移动步骤(3个盘子):");
HanoiTower(3, 'A', 'B', 'C');
// 7. 目录结构显示
Console.WriteLine("\n7. 显示目录结构:");
string path = @"C:\Example"; // 替换为实际路径
try
{
ShowDirectoryStructure(path, 0);
}
catch (Exception ex)
{
Console.WriteLine($"读取目录时出错: {ex.Message}");
}
Console.ReadKey();
}
// 1. 计算阶乘
static int CalculateFactorial(int n)
{
if (n <= 1) return 1;
return n * CalculateFactorial(n - 1);
}
// 2. 斐波那契数列
static int Fibonacci(int n)
{
if (n <= 1) return n;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
// 3. 数组求和
static int ArraySum(int[] array, int index)
{
if (index < 0) return 0;
return array[index] + ArraySum(array, index - 1);
}
// 4. 字符串反转
static string ReverseString(string str)
{
if (string.IsNullOrEmpty(str) || str.Length <= 1)
return str;
return ReverseString(str.Substring(1)) + str[0];
}
// 5. 最大公约数(欧几里得算法)
static int GCD(int a, int b)
{
if (b == 0) return a;
return GCD(b, a % b);
}
// 6. 汉诺塔问题
static void HanoiTower(int n, char from, char auxiliary, char to)
{
if (n == 1)
{
Console.WriteLine($"将盘子 1 从 {from} 移动到 {to}");
return;
}
HanoiTower(n - 1, from, to, auxiliary);
Console.WriteLine($"将盘子 {n} 从 {from} 移动到 {to}");
HanoiTower(n - 1, auxiliary, from, to);
}
// 7. 显示目录结构
static void ShowDirectoryStructure(string path, int level)
{
// 添加缩进
string indent = new string(' ', level * 2);
try
{
// 显示当前目录
DirectoryInfo dir = new DirectoryInfo(path);
Console.WriteLine($"{indent}[{dir.Name}]");
// 显示文件
foreach (FileInfo file in dir.GetFiles())
{
Console.WriteLine($"{indent} {file.Name}");
}
// 递归显示子目录
foreach (DirectoryInfo subDir in dir.GetDirectories())
{
ShowDirectoryStructure(subDir.FullName, level + 1);
}
}
catch (UnauthorizedAccessException)
{
Console.WriteLine($"{indent}访问被拒绝");
}
}
}
关键点:
-
基例(终止条件): 递归必须要有一个明确的终止条件,即基例,否则会导致无限递归
-
递归关系: 问题的解可以表示为其子问题的解的组合
-
堆栈开销: 每一次递归调用都会在调用栈中存储当前函数的状态,深入太深可能导致栈溢出
优势:
- 代码简洁: 对于某些问题,递归能使代码更易读、更简洁
- 自然适应某些算法: 如树的遍历、分治算法等,递归实现更符合逻辑
注意:
- 效率问题: 递归可能导致大量的函数调用,增加开销。
- 栈溢出风险: 如果递归深度太大,可能导致栈溢出错误。
三.回调
回调是指将一个函数作为参数传递给另一个函数,当特定事件发生或条件满足时调用该函数
在C#中,回调通常通过委托和事件来实现
回调示例:
class Program
{
// 定义委托类型
public delegate void ProcessCompleted(string result);
public delegate int CalculateDelegate(int x, int y);
static void Main(string[] args)
{
Console.WriteLine("回调示例:\n");
// 1. 使用委托回调
Console.WriteLine("1. 委托回调示例:");
ProcessCompleted callback = ShowResult;
ProcessWithCallback("任务1", callback);
// 2. 使用Action/Func
Console.WriteLine("\n2. Action/Func回调示例:");
ProcessWithAction("任务2", (result) =>
{
Console.WriteLine($"Lambda回调结果: {result}");
});
// 3. 事件回调
var processor = new TaskProcessor();
processor.OnCompleted += (sender, result) =>
{
Console.WriteLine($"事件回调结果: {result}");
};
processor.StartProcess("任务3");
// 4. 异步回调
Console.WriteLine("\n4. 异步回调示例:");
AsyncProcessDemo().Wait();
// 5. 计算器回调示例
Console.WriteLine("\n5. 计算器回调示例:");
CalculateWithCallback(10, 5, Add);
CalculateWithCallback(10, 5, Subtract);
Console.ReadKey();
}
// 基本回调方法
static void ShowResult(string result)
{
Console.WriteLine($"普通回调结果: {result}");
}
// 使用委托的处理方法
static void ProcessWithCallback(string input, ProcessCompleted callback)
{
// 模拟处理过程
Thread.Sleep(100);
string result = $"处理完成: {input}";
callback(result);
}
// 使用Action的处理方法
static void ProcessWithAction(string input, Action<string> callback)
{
Thread.Sleep(100);
string result = $"处理完成: {input}";
callback(result);
}
// 异步回调示例
static async Task AsyncProcessDemo()
{
await ProcessAsync("异步任务", (result) =>
{
Console.WriteLine($"异步回调结果: {result}");
});
}
static Task ProcessAsync(string input, Action<string> callback)
{
return Task.Run(() =>
{
Thread.Sleep(100);
string result = $"处理完成: {input}";
callback(result);
});
}
// 计算器回调方法
static int Add(int x, int y) => x + y;
static int Subtract(int x, int y) => x - y;
static void CalculateWithCallback(int x, int y, CalculateDelegate callback)
{
int result = callback(x, y);
Console.WriteLine($"计算结果: {result}");
}
}
// 使用事件的处理器类
class TaskProcessor
{
public event EventHandler<string> OnCompleted;
public void StartProcess(string input)
{
// 模拟处理过程
Thread.Sleep(100);
string result = $"处理完成: {input}";
// 触发事件
OnCompleted?.Invoke(this, result);
}
}
关键点:
-
委托(Delegate): 在C#中,回调通常通过委托实现,委托是对函数的引用,可以作为参数传递
-
事件(Event): 事件是委托的特殊形式,用于发布和订阅机制,常用于GUI编程、异步操作等
-
异步编程: 回调在异步编程中非常重要,可以在异步操作完成后执行后续处理
优势:
- 解耦: 回调机制使得调用者和被调用者之间的依赖减少,代码更加灵活
- 灵活性: 可以在运行时决定调用哪个函数,增强了代码的可扩展性
应用场景:
- 事件处理: 例如按钮点击事件,网络请求完成事件等
- 异步操作: 在异步编程中,回调函数用于在任务完成后继续执行后续逻辑
4.三种模式的特点对比:
迭代:
特点:
- 逐个处理元素
- 内存效率高
- 代码直观
适用场景:
- 集合遍历
- 简单重复操作
- 需要延迟计算
递归:
特点:
- 自调用
- 代码简洁
- 需要注意栈溢出
适用场景:
- 树形结构处理
- 分治算法
- 自然递归问题
回调:
特点:
- 灵活可配置
- 解耦操作
- 支持异步
适用场景:
- 事件处理
- 异步操作
- 策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式使得算法可以在不影响客户端的情况下发生变化。它通过定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换,从而让算法的变化不会影响到使用算法的客户。