
1. 委托与接口的本质解析在软件开发领域委托(delegate)和接口(interface)是两个看似简单却常被误解的核心概念。我见过太多开发者能写出委托和接口的代码却说不出它们的设计哲学和适用场景的区别。这就像会开手动挡车却不懂离合器原理——短期能用长期受限。委托本质上是一种类型安全的函数指针它允许将方法作为参数传递。在C#中委托的声明方式delegate int Calculate(int x, int y);实际上定义了一个能指向任何接收两个int参数并返回int的方法的类型。这种间接调用机制是事件模型和回调机制的基石。接口则是一组相关功能的契约它只定义成员签名而不包含实现。当一个类实现接口时它承诺提供这些功能的具体实现。接口的核心价值在于解耦——调用方只需要知道接口定义不需要关心具体实现类。关键区别委托关注方法签名匹配接口关注行为契约实现。委托用于方法级别的间接调用接口用于类型级别的多态设计。2. 委托的深度应用场景2.1 回调机制的实现在异步编程中委托是回调的天然载体。比如文件读取完成后需要执行的操作public delegate void FileReadCallback(string content); public void ReadFileAsync(string path, FileReadCallback callback) { // 异步读取文件... string content file data; callback(content); // 通过委托触发回调 }这种模式避免了轮询检查使代码更高效。我在日志系统项目中就采用这种设计当日志文件滚动(rollover)时通过委托通知所有订阅者。2.2 事件系统的底层支撑C#的事件本质上是特殊类型的委托字段public class Button { public event EventHandler Clicked; protected virtual void OnClicked() { Clicked?.Invoke(this, EventArgs.Empty); } }这里EventHandler本身就是个委托类型。事件语法糖保证了/-操作的线程安全这是纯委托无法自动提供的。2.3 高阶函数与LINQ委托使C#支持函数式编程范式。比如Where方法的签名public static IEnumerableTSource WhereTSource( this IEnumerableTSource source, FuncTSource, bool predicate)这里的FuncTSource, bool就是个泛型委托允许传入任意符合签名的方法。正是这种设计让LINQ如此灵活。3. 接口的设计哲学与实践3.1 契约优先的设计方法良好的接口设计应该遵循ISP(接口隔离原则)。我曾重构过一个违反此原则的支付系统// 不良设计 public interface IPayment { void CreditCardPay(); void Alipay(); void WechatPay(); void QueryBalance(); void Refund(); } // 优化后 public interface IPaymentMethod { void Pay(); } public interface IBalanceQuery { decimal GetBalance(); } public interface IRefundable { void Refund(); }拆分后各司其职避免了胖接口问题。3.2 多态实现的基石接口最强大的能力是让不同类型对象通过相同接口交互public interface ILogger { void Log(string message); } public class FileLogger : ILogger { ... } public class DatabaseLogger : ILogger { ... } public class Service { private readonly ILogger _logger; public Service(ILogger logger) { _logger logger; // 依赖注入 } }这种设计使得更换日志实现无需修改Service类符合开闭原则。3.3 接口的进阶用法C# 8.0引入了默认接口方法允许在接口中提供默认实现public interface IOrderProcessor { void Validate(Order order); void Process(Order order) { Validate(order); // 默认处理逻辑 } }这在维护向后兼容性时非常有用但需谨慎使用以避免接口污染。4. 委托与接口的对比决策4.1 何时选择委托以下场景更适合使用委托需要回调或事件通知机制时要实现策略模式但策略很简单(单个方法)LINQ查询或函数式编程场景需要动态改变方法行为时比如插件系统的加载回调public delegate void PluginLoadedHandler(IPlugin plugin); public class PluginManager { public event PluginLoadedHandler PluginLoaded; public void LoadPlugin(string path) { var plugin LoadFromPath(path); PluginLoaded?.Invoke(plugin); } }4.2 何时选择接口以下场景更适合使用接口需要定义一组相关操作时要实现真正的多态行为需要建立长期稳定的抽象契约时涉及依赖注入或服务定位时比如数据访问层的抽象public interface IRepositoryT { T GetById(int id); void Add(T entity); void Update(T entity); void Delete(int id); }4.3 性能考量在性能敏感场景需注意委托调用比直接方法调用稍慢(纳秒级)接口虚方法调用比委托调用更快对于高频调用的简单操作考虑使用泛型接口而非委托5. 实战中的陷阱与解决方案5.1 委托的常见问题内存泄漏忘记取消事件订阅会导致对象无法被GC回收。解决方法// 订阅 target.Event Handler; // 必须配对的取消订阅 target.Event - Handler;多线程问题委托调用不是线程安全的。解决方案var handlers SomeEvent?.GetInvocationList(); if (handlers ! null) { foreach (var handler in handlers) { try { handler.DynamicInvoke(this, args); } catch { /* 处理异常 */ } } }5.2 接口的常见陷阱接口污染过度使用接口导致系统复杂。建议只有当多个类需要实现相同契约时才提取接口避免为每个类都创建对应接口遵循YAGNI原则(You Arent Gonna Need It)版本控制问题接口一旦发布就很难修改。应对策略使用默认接口方法(C# 8.0)考虑接口继承创建新版本设计时充分预留扩展空间5.3 混合使用的最佳实践在复杂系统中委托和接口可以协同工作public interface IDataProcessor { event EventHandler ProcessingStarted; event EventHandler ProcessingCompleted; void ProcessData(FuncDataItem, bool filter); }这种设计既提供了结构化契约(接口)又保留了灵活性(委托)。6. 现代开发中的演进趋势6.1 函数式编程的影响随着LINQ和异步编程的普及委托的使用变得更加广泛。新的Func和Action泛型委托减少了自定义委托类型的需要。6.2 接口的默认方法C# 8.0引入的默认接口方法改变了接口的传统定义使其具有一定实现能力这在API版本控制中非常有用。6.3 源生成器与委托新的C#编译器特性如源生成器(source generators)大量使用委托来进行编译时代码生成和转换。6.4 跨平台开发中的接口在Xamarin/MAUI等跨平台框架中接口是实现平台特定代码的推荐方式public interface IDeviceService { string GetDeviceId(); } // Android实现 public class AndroidDeviceService : IDeviceService { ... } // iOS实现 public class iOSDeviceService : IDeviceService { ... }7. 设计模式中的典型应用7.1 观察者模式的双重实现观察者模式既可以用委托/事件实现public class Subject { public event EventHandler StateChanged; }也可以用接口实现public interface IObserver { void Update(); } public class Subject { private ListIObserver _observers new(); public void AddObserver(IObserver o) _observers.Add(o); public void Notify() _observers.ForEach(o o.Update()); }委托版本更简洁接口版本更显式且类型安全。7.2 策略模式的两种形式简单策略可以用委托public class Sorter { public FuncListint, Listint SortStrategy { get; set; } public Listint Sort(Listint input) SortStrategy(input); }复杂策略更适合接口public interface ISortStrategy { Listint Sort(Listint input); } public class Sorter { private ISortStrategy _strategy; public Sorter(ISortStrategy strategy) { _strategy strategy; } }7.3 命令模式的实现选择命令模式通常用接口public interface ICommand { void Execute(); void Undo(); }但对于简单命令委托也能胜任public class Command { private Action _execute; private Action _undo; public Command(Action execute, Action undo) { _execute execute; _undo undo; } }选择取决于命令的复杂度和是否需要状态维护。8. 性能优化的关键点8.1 委托调用的开销每次委托调用都涉及额外的间接寻址。在循环中高频调用时可以考虑// 优化前 for(int i0; i1_000_000; i) { action(); } // 优化后 var localAction action; // 缓存委托实例 for(int i0; i1_000_000; i) { localAction(); }8.2 接口虚方法表JIT编译器会为接口调用生成虚方法表(vtable)现代运行时已经高度优化这种调用。但大量小接口仍会影响性能。8.3 结构体实现接口当值类型实现接口时会发生装箱需要注意struct ValueType : IInterface {} IInterface obj new ValueType(); // 装箱发生8.4 委托组合的代价多播委托()会创建新的委托实例组合原有调用列表。在性能关键路径上应避免频繁组合操作。9. 单元测试中的不同策略9.1 测试委托参数使用委托参数的方法更容易测试因为可以直接传入测试double[Test] public void TestMethodWithCallback() { bool callbackCalled false; Action callback () callbackCalled true; TestedMethod(callback); Assert.IsTrue(callbackCalled); }9.2 测试接口实现接口允许使用Mock框架进行更复杂的测试[Test] public void TestServiceWithMockRepository() { var mockRepo new MockIRepository(); mockRepo.Setup(r r.GetById(1)).Returns(new Item()); var service new Service(mockRepo.Object); var result service.GetItem(1); Assert.NotNull(result); }9.3 测试事件系统测试事件需要订阅并验证是否触发[Test] public void TestEventTrigger() { var publisher new EventPublisher(); bool eventFired false; publisher.MyEvent (s,e) eventFired true; publisher.DoSomethingThatShouldTriggerEvent(); Assert.IsTrue(eventFired); }10. 架构设计中的选择指南10.1 分层架构中的接口使用在典型的三层架构中表现层通过接口依赖业务逻辑层业务逻辑层通过接口依赖数据访问层接口定义应该放在被依赖方所在的程序集10.2 插件系统中的委托应用插件系统常结合两者用接口定义插件契约用委托处理插件生命周期事件public interface IPlugin { string Name { get; } void Initialize(); } public class PluginHost { public event ActionIPlugin PluginInitialized; public void LoadPlugin(IPlugin plugin) { plugin.Initialize(); PluginInitialized?.Invoke(plugin); } }10.3 微服务通信中的接口抽象在微服务架构中服务客户端通常实现接口以抽象远程调用回调通知可以使用委托模型接口更适合定义服务契约public interface IOrderService { TaskOrder GetOrderAsync(int id); event EventHandlerOrderUpdatedEventArgs OrderUpdated; }11. 跨语言视角的比较11.1 Java中的对应概念Java没有直接的委托对应物但通过函数式接口(FunctionalInterface)和lambda表达式实现了类似功能。接口在Java中更为核心。11.2 JavaScript/TypeScript的实现JavaScript中函数是一等公民天然支持委托模式。TypeScript的接口主要用于类型检查不产生运行时开销。11.3 C的实现方式C通过函数指针、std::function和抽象基类分别实现类似委托和接口的功能但缺乏语言层面的直接支持。12. 历史演变与未来方向12.1 .NET中的发展历程.NET 1.0引入委托和基本接口.NET 2.0泛型委托(Action/Func)和泛型接口C# 3.0lambda表达式简化委托C# 8.0默认接口方法12.2 其他语言的借鉴Swift的protocol、Rust的trait都受到接口概念的启发但各有创新。Kotlin的函数类型提供了比C#委托更灵活的表达。12.3 未来可能的增强可能会看到更轻量级的委托语法接口的更多实现能力更好的委托组合操作符接口的静态抽象成员13. 代码生成与反射中的应用13.1 动态创建委托通过MethodInfo.CreateDelegate可以运行时创建委托var method typeof(MyClass).GetMethod(MyMethod); var del (Action)method.CreateDelegate(typeof(Action));13.2 反射接口实现通过Type.GetInterface可以检查接口实现var type typeof(MyClass); var iface type.GetInterface(IMyInterface); if (iface ! null) { // 类型实现了接口 }13.3 Expression Tree与委托表达式树可以动态编译为委托ExpressionFuncint, int square x x * x; var compiled square.Compile(); int result compiled(5); // 2514. 序列化与持久化考量14.1 委托的序列化限制委托通常不可序列化解决方案改为序列化方法名和类型信息使用弱引用模式重建时重新绑定委托14.2 接口的持久化策略持久化接口引用的对象时需要保存具体类型信息反序列化时需能解析具体类型可能需要引入DTO(数据传输对象)15. 设计原则的具体体现15.1 单一职责原则委托天然符合SRP——一个委托只代表一个方法签名。接口则应保持专注避免上帝接口。15.2 开闭原则通过接口扩展系统比修改现有接口更符合OCP。委托也支持在不修改现有代码的情况下添加新行为。15.3 依赖倒置原则高层模块不应依赖低层模块两者都应依赖抽象。接口是实现DIP的主要手段而委托可以用于解耦具体实现。15.4 接口隔离原则客户端不应被迫依赖它们不用的方法。这意味着接口应该小而专注这正是委托的天然特性。16. 调试技巧与工具支持16.1 委托调用栈分析调试多播委托时可以使用Delegate.GetInvocationList()检查所有注册的方法var delegates myDelegate.GetInvocationList(); foreach (var del in delegates) { Console.WriteLine(del.Method.Name); }16.2 接口实现检查Visual Studio的Go to Implementation功能可以快速导航到接口的具体实现。ReSharper还提供Find Implementations功能。16.3 性能分析工具使用性能探查器可以识别委托调用的热点分析接口调用的开销比较不同实现的性能特征17. 代码质量指标17.1 委托相关的指标委托链长度(避免过长的多播委托)委托分配频率(高频分配可能影响GC)委托缓存情况(是否适当缓存了委托实例)17.2 接口相关的指标接口方法数量(建议不超过5-7个)接口继承深度(建议不超过2层)接口实现分散度(避免一个类实现太多接口)18. 团队协作规范18.1 委托使用指南公共API中优先使用标准委托类型(Func/Action)自定义委托类型应具有描述性名称事件委托应遵循EventHandler模式18.2 接口设计规范接口名称应以I开头避免标记接口(没有方法的接口)为接口提供XML文档注释考虑为接口添加单元测试18.3 代码审查要点审查时应检查委托是否适当地取消订阅接口是否遵循ISP原则是否混淆了委托和接口的适用场景是否有不必要的接口或委托定义19. 重构技巧19.1 从委托升级到接口当委托参数过多或逻辑复杂时考虑重构为接口// 重构前 public void Process(FuncInput, Output processor) { ... } // 重构后 public interface IProcessor { Output Process(Input input); } public void Process(IProcessor processor) { ... }19.2 接口简化策略对于过于复杂的接口可以拆分为多个小接口用抽象类提供部分实现将某些方法转为扩展方法19.3 委托链优化对于多播委托如果某些处理器耗时较长可以考虑异步调用引入责任链模式并行处理(当处理器无依赖时)20. 领域特定应用20.1 GUI开发中的模式在WinForms/WPF中事件委托控件接口抽象交互方式命令模式常结合两者20.2 游戏开发中的应用游戏引擎中输入处理常用委托回调游戏对象交互通过接口事件系统构建在委托之上20.3 金融系统的实现高频交易系统策略模式用接口定义价格回调用委托通知需要特别注意委托性能21. 安全考量21.1 委托的安全风险恶意代码可能订阅关键事件解决方案实现显式的订阅控制public event EventHandler CriticalEvent { add { if (IsTrustedCaller()) { _criticalEvent value; } } remove { _criticalEvent - value; } }21.2 接口的安全设计接口可能暴露过多信息考虑使用显式接口实现隐藏敏感成员可以定义不同的安全级别接口22. 并发编程模式22.1 委托的线程安全多播委托的/-是线程安全的但调用不是。解决方案event EventHandler MyEvent; public void RaiseEvent() { var handlers MyEvent; if (handlers ! null) { foreach (EventHandler handler in handlers.GetInvocationList()) { handler.BeginInvoke(this, EventArgs.Empty, null, null); } } }22.2 接口的并发实现实现接口的类需要自行处理线程安全public class ThreadSafeService : IService { private readonly object _lock new object(); public void CriticalMethod() { lock (_lock) { // 线程安全操作 } } }23. 依赖注入中的应用23.1 委托工厂模式在某些DI场景下可以用委托工厂替代接口public class Client { private readonly FuncIService _serviceFactory; public Client(FuncIService serviceFactory) { _serviceFactory serviceFactory; } public void DoWork() { using var service _serviceFactory(); service.Execute(); } }23.2 接口注入的最佳实践构造函数注入是首选属性注入应避免(除非可选依赖)方法注入适合短期依赖public class Consumer { private readonly IDependency _dep; public Consumer(IDependency dep) { _dep dep; } }24. 元编程技术24.1 动态创建委托使用DynamicMethod可以在运行时创建和编译新方法var dynamicMethod new DynamicMethod(Test, null, null); var il dynamicMethod.GetILGenerator(); il.EmitWriteLine(Hello, World!); il.Emit(OpCodes.Ret); var del (Action)dynamicMethod.CreateDelegate(typeof(Action)); del();24.2 接口的动态实现通过Emit可以动态创建接口实现var tb AssemblyBuilder.DefineDynamicAssembly(...); // 构建实现接口的类型...25. 编译器优化解析25.1 委托的编译过程C#编译器将委托转换为继承自MulticastDelegate的类包含目标对象引用方法指针调用列表管理25.2 接口方法分派JIT编译器使用虚方法表(vtable)实现接口方法调用现代运行时优化了此过程以减少开销。25.3 泛型委托的特化对于泛型委托如FuncTJIT会为每个值类型T生成特化版本避免装箱。26. 内存模型分析26.1 委托的内存占用每个委托实例包含目标对象引用(实例方法)方法指针可能的调用列表26.2 接口引用的本质接口变量存储对象引用类型信息(用于方法分派)26.3 闭包的内存影响lambda表达式捕获局部变量时会生成闭包类可能导致意外内存保留void Method() { var bigObject new BigObject(); Action action () Console.WriteLine(bigObject); // bigObject被闭包保留直到action不再被引用 }27. 跨平台兼容性27.1 AOT编译下的委托在iOS等AOT平台动态创建委托可能受限需预生成所需委托。27.2 接口的跨平台一致性接口行为在所有平台上一致是跨平台代码的理想抽象方式。27.3 本机互操作中的使用在P/Invoke场景中委托可用于本机回调接口通常不直接用于互操作[DllImport(lib)] public static extern void RegisterCallback([MarshalAs(...)] Actionint callback);28. 语言互操作28.1 从C#调用其他语言的函数通过委托可以包装C函数指针等[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void NativeCallback(int value);28.2 其他语言使用C#接口COM互操作中C#接口可以暴露给其他语言[ComVisible(true)] public interface IMyComInterface { void ComMethod(); }29. 领域驱动设计中的应用29.1 领域事件实现领域事件常用委托模型public class Order { public event EventHandlerOrderPaidEventArgs Paid; public void MarkAsPaid() { // ...支付逻辑 Paid?.Invoke(this, new OrderPaidEventArgs(this)); } }29.2 仓储接口定义DDD中的仓储模式通常用接口定义public interface IOrderRepository { Order GetById(OrderId id); void Save(Order order); IEnumerableOrder GetPendingOrders(); }29.3 规约模式实现可以用委托或接口实现规约模式// 委托方式 public delegate bool SpecificationT(T item); // 接口方式 public interface ISpecificationT { bool IsSatisfiedBy(T item); }30. 现代C#特性影响30.1 本地函数与委托C# 7.0的本地函数可以作为更高效的委托替代public void Method() { int local 42; void LocalFunction() Console.WriteLine(local); Action action LocalFunction; // 比lambda更高效 }30.2 模式匹配增强C#的模式匹配可以简化接口类型检查if (obj is ILogger logger) { logger.Log(message); }30.3 可空引用类型对接口和委托的影响public interface IService { string? GetNullableString(); // 可空返回 } public delegate void Callback(string? message); // 可空参数31. 编译器代码分析31.1 委托转换规则编译器处理委托转换时协变允许返回类型更具体逆变允许参数类型更通用方法签名必须匹配31.2 接口映射算法当类实现接口时编译器查找显式实现方法检查签名兼容性处理多接口同名方法冲突31.3 类型推断行为对于泛型委托类型推断规则复杂Funcint, string func i i.ToString(); // 推断正确32. 调试符号与堆栈32.1 委托调用堆栈调试时委托调用会显示在堆栈中但可能缺少直观信息。可以通过以下方式增强public class NamedDelegate { public string Name { get; } public Action Action { get; } public void Invoke() { try { Action(); } catch (Exception ex) { throw new Exception($Error in {Name}, ex); } } }32.2 接口方法调试接口方法调试与普通方法类似但需注意调用可能被优化实际类型可能影响行为断点应设在实现而非接口定义33. 异常处理模式33.1 委托调用中的异常多播委托调用中一个处理器异常会中断整个调用链。解决方案foreach (var handler in myDelegate.GetInvocationList()) { try { handler.DynamicInvoke(args); } catch (Exception ex) { // 记录并继续 } }33.2 接口实现的异常契约接口应明确记录可能抛出的异常/// exception crefInvalidOperationException当...时抛出/exception public interface IOperation { void Execute(); }34. 文档生成技巧34.1 委托的文档标准委托文档应说明预期用途参数含义返回值意义可能的副作用/// summary /// 处理数据项的回调 /// /summary /// param nameitem要处理的数据项/param /// returns处理是否成功/returns public delegate bool DataProcessor(DataItem item);34.2 接口的文档实践接口文档应包括契约目的前置条件后置条件实现要求/// summary /// 提供数据缓存功能 /// /summary /// remarks /// 实现必须是线程安全的 /// /remarks public interface ICache { /// summary从缓存获取数据/summary object? Get(string key); }35. 代码生成技术35.1 自动生成委托代码T4模板或源生成器可以为常用签名创建委托生成委托转换代码创建委托组合工具方法35.2 接口的代码生成代码生成适用于重复性接口实现包装器/适配器模式基于元数据的动态接口36. 设计时工具支持36.1 委托的IDE功能现代IDE提供Lambda表达式转换委托签名验证用法查找36.2 接口的工具支持包括实现接口的快速修复提取接口重构接口导航37. 测试替身策略37.1 委托的测试替身在测试中可轻松创建委托替身[Test] public void TestWithDelegateStub() { bool called false; Action stub () called true; TestedMethod(stub); Assert.IsTrue(called); }37.2 接口的Mock对象使用Mock框架创建接口Mock[Test] public void TestWithMock() { var mock new MockIService(); mock.Setup(s s.GetData()).Returns(test); var result TestedMethod(mock.Object); Assert.AreEqual(TEST, result); }38. 架构决策记录38.1 选择委托的ADR架构决策记录示例标题使用委托实现回调机制 状态已采纳 背景需要灵活的事件通知机制... 决策采用委托而非接口因为... 影响更简单的订阅模型但需注意内存管理...38.2 选择接口的ADR标题核心服务抽象采用接口 状态已采纳 背景需要支持多种实现... 决策使用接口定义服务契约... 影响更好的解耦但增加抽象层...39. 代码异味识别39.1 委托相关的异味过长的委托链频繁的委托分配忽略委托返回值未处理的委托异常39.2 接口相关的异味接口中过多方法重复的接口定义空接口实现接口方法总是抛出NotImplementedException40. 重构到模式的路径40.1 从委托到观察者模式当事件处理逻辑复杂时定义事件参数类创建专门的处理器接口引入事件聚合器40.2 从接口到策略模式当接口实现代表不同算法时识别变化点封装每种算法到独立类通过接口统一调用41. 性能模式选择41.1 高频调用的优化对于高频调用场景考虑使用结构体实现接口(当可行时)缓存委托实例避免接口的深层继承41.2 内存敏感环境在内存受限环境中减少委托分配使用显式接口实现避免大型接口42. 兼容性设计42.1 版本兼容的委托维护委托兼容性需避免更改签名提供过载而非修改文档化变更42.2 接口的版本策略接口版本控制方法添加新接口继承旧接口使用默认接口方法提供适配器43. 跨版本演变案例43.1 .NET 1.1到4.0的委托.NET 1.1自定义委托类型.NET 2.0泛型委托.NET 3.5Lambda表达式.NET 4.0协变/逆变支持43.2 接口的演变早期纯抽象C# 8.0默认方法未来可能静态接口成员44. 元数据与反射44.1 委托的反射信息通过反射可以获取委托的目标方法检查委托签名动态调用委托44.2 接口的反射分析反射接口可以获取所有实现类型检查接口继承层次验证契约实现45. 动态编程模型45.1 DLR中的委托动态语言运行时中委托可绑定到动态方法支持动态调用约定允许更灵活的签名45.2 动态接口实现通过ExpandoObject等可以动态实现接口运行时添加成员模拟静态类型46. 编译器内部机制46.1 委托的编译输出编译器生成继承MulticastDelegate的类Invoke/BeginInvoke/EndInvoke方法必要的元数据46.2 接口的CLR实现CLR层面接口方法表布局接口映射表虚方法分派47. 运行时类型处理47.1 委托的类型安全运行时检查参数类型兼容性返回类型协变调用目标有效性47.2 接口的类型转换as/is操作符涉及接口映射检查类型兼容验证空值处理48. 高级优化技术48.1 委托的内联优化某些情况下JIT可以内联单播委托调用缓存委托调用目标优化闭包访问48.2 接口的去虚拟化当具体类型已知时JIT可能去虚拟化接口调用直接调用具体方法消除间接调用开销49. 真实项目经验在电商平台项目中我们曾用接口抽象不同支付网关public interface IPaymentGateway { PaymentResult Process(PaymentRequest request); TaskPaymentResult ProcessAsync(PaymentRequest request); } // 支付宝实现 public class AlipayGateway : IPaymentGateway { ... } // 微信支付实现 public class WechatPayGateway : IPaymentGateway { ... }同时使用委托处理支付状态变更通知public class PaymentService { public event EventHandlerPaymentStatusChangedEventArgs StatusChanged; private void OnStatusChanged(Payment payment) { StatusChanged?.Invoke(this, new PaymentStatusChangedEventArgs(payment)); } }这种混合设计既保持了核心支付逻辑的稳定性又提供了灵活的通知机制。50. 终极决策指南经过多年实践我总结出以下决策流程是否需要将方法作为参数传递是 → 使用委托否 → 进入下一步是否需要定义一组相关操作