共计 2496 个字符,预计需要花费 7 分钟才能阅读完成。
常见痛点分析
在 C# 开发中,主函数直接调用其他函数时经常会遇到以下几个问题:

- 紧密耦合:主函数与被调用函数之间高度依赖,修改一个函数可能影响整个调用链
- 难以测试:由于耦合度高,单元测试难以隔离特定功能进行验证
- 同步调用阻塞:长时间运行的同步操作会阻塞主线程,导致 UI 冻结或服务响应延迟
技术解决方案
1. 方法封装的最佳实践
良好的封装是降低耦合度的第一道防线。我们应该遵循单一职责原则(SRP),将功能拆分为独立的单元:
// 不良实践:功能混杂
public void ProcessOrder()
{
// 验证订单
// 计算价格
// 保存数据库
// 发送通知
}
// 良好实践:职责分离
public class OrderProcessor
{
private readonly IOrderValidator _validator;
private readonly IPriceCalculator _calculator;
private readonly IOrderRepository _repository;
private readonly INotificationService _notifier;
public OrderProcessor(IOrderValidator validator, /*... 其他依赖 */)
{// 依赖注入}
public async Task ProcessAsync(Order order)
{_validator.Validate(order);
order.Total = _calculator.Calculate(order);
await _repository.SaveAsync(order);
await _notifier.SendAsync(order);
}
}
2. 委托与 Lambda 表达式的应用
委托和 Lambda 提供了灵活的调用方式,适用于不同场景:
// 简单委托示例
public delegate int CalculationDelegate(int x, int y);
public void Main()
{
// 方法组转换
CalculationDelegate add = AddNumbers;
// Lambda 表达式
CalculationDelegate multiply = (x, y) => x * y;
// 实际应用:策略模式
ProcessWithStrategy(5, 3, add);
ProcessWithStrategy(5, 3, multiply);
}
private int AddNumbers(int a, int b) => a + b;
private void ProcessWithStrategy(int a, int b, CalculationDelegate strategy)
{Console.WriteLine($"结果: {strategy(a, b)}");
}
3. Task 异步编程模型
异步编程可以显著提高应用程序的响应能力:
public async Task MainAsync()
{
// 并行执行多个独立任务
var task1 = ProcessDataAsync(source1);
var task2 = ProcessDataAsync(source2);
await Task.WhenAll(task1, task2);
// 顺序执行有依赖关系的任务
var result1 = await Step1Async();
var result2 = await Step2Async(result1);
}
private async Task ProcessDataAsync(string source)
{
// 模拟耗时操作
await Task.Delay(1000);
Console.WriteLine($"处理完成: {source}");
}
性能对比测试
我们通过一个简单的基准测试来比较同步和异步调用的性能差异:
[MemoryDiagnoser]
public class CallBenchmark
{[Benchmark]
public void SyncCalls()
{for (int i = 0; i < 1000; i++)
{Thread.Sleep(1); // 模拟 IO 操作
}
}
[Benchmark]
public async Task AsyncCalls()
{var tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
{tasks.Add(Task.Delay(1)); // 模拟异步 IO
}
await Task.WhenAll(tasks);
}
}
测试结果(Release 模式,.NET 6):
| 方法 | 耗时(ms) | 内存分配 |
|---|---|---|
| SyncCalls | 1050 | 16 B |
| AsyncCalls | 35 | 32 KB |
虽然异步调用有更高的内存开销,但在并发场景下性能优势明显。
生产环境避坑指南
1. 异步异常处理
异步方法中的异常需要通过 await 传播:
try
{await DangerousOperationAsync();
}
catch (CustomException ex)
{
// 处理特定异常
Logger.LogError(ex, "操作失败");
}
2. 死锁预防
避免在 UI 线程或 ASP.NET 请求上下文中使用.Result 或.Wait():
// 错误做法 - 可能导致死锁
var result = GetDataAsync().Result;
// 正确做法
var result = await GetDataAsync();
3. 上下文切换控制
对于不依赖上下文的代码,可以使用 ConfigureAwait(false):
public async Task<string> GetDataAsync()
{var data = await DownloadAsync().ConfigureAwait(false);
return Process(data); // 后续代码不需要原始上下文
}
调用方式选择思考
在实际项目中,我们需要根据具体场景选择合适的调用方式:
- 同步调用 适合:
- 简单控制台应用
- 执行时间极短的操作
-
必须顺序执行的逻辑
-
异步调用 适合:
- UI 应用程序(保持响应)
- Web 服务(提高吞吐量)
- IO 密集型操作(文件、网络访问)
你的项目中是如何权衡这些因素的?是否有哪些特别的性能优化经验可以分享?
正文完
