C设计实践:方法、操作符、事件与异常处理
立即解锁
发布时间: 2025-08-18 00:24:51 阅读量: 2 订阅数: 3 

### C# 设计实践:方法、操作符、事件与异常处理
在 C# 编程中,合理设计方法、操作符、事件以及正确处理异常是构建高效、健壮代码的关键。下面将详细探讨这些方面的设计原则和实践方法。
#### 1. 方法重载与操作符重载
在设计 API 时,方法重载应谨慎使用。如果只有二十分之一的开发者能正确识别一系列重载方法中调用的是哪个重载,那么这个 API 显然过于复杂。为用户提供完整功能时,应创建最少的重载集,避免添加过多方法增加库的复杂度而不提升其实用性。
操作符重载方面,不同面向对象语言对操作符重载的态度不同。C# 采取折中的方式,部分操作符可以重载。但由于不同语言对操作符重载的处理不同,公共语言规范(CLS)规定重载操作符不符合 CLS 标准,不过每个操作符都映射到一个特殊的方法名,以便不支持操作符重载的语言调用。
以下是一些常见操作符重载的规则:
- **相等操作符(== 和 !=)**:如果类型重写了 `System.Object.Equals` 或实现了 `IEquatable<T>`,则应重载 `==` 操作符,同时必须重载 `!=` 操作符并重写 `System.Object.GetHashCode()`,且这三个方法必须遵循相同的相等语义。
- **比较操作符(<、>、<= 和 >=)**:如果类型实现了 `IComparable<T>`,应重载 `<` 和 `>` 操作符;若还实现了 `IEquatable<T>`,则还应重载 `<=` 和 `>=` 操作符,且这些操作符必须成对重载。
在决定是否重载操作符时,应先定义支持的方法集,再根据方法来推导要支持的操作符。例如,定义了 `Add()` 和 `Subtract()` 方法,可考虑定义 `+` 和 `-` 操作符。同时,要为不支持操作符重载的语言提供替代方案,优先使用方法而非操作符重载。
#### 2. 事件与运行时耦合
事件看似能让类与需要通知的类型完全解耦,但实际上存在耦合问题。事件参数类型可能包含状态标志,指示类执行特定操作。例如,在 `WorkerEngine` 类中,事件订阅者可能会相互影响,最后一个订阅者可能会覆盖其他订阅者的请求。
```csharp
public class WorkerEngine
{
public event EventHandler<WorkerEventArgs> OnProgress;
public void DoLotsOfStuff()
{
for (int i = 0; i < 100; i++)
{
SomeWork();
WorkerEventArgs args = new WorkerEventArgs();
args.Percent = i;
EventHandler<WorkerEventArgs> progHandler = OnProgress;
if (progHandler != null)
{
progHandler(this, args);
}
if (args.Cancel)
return;
}
}
private void SomeWork()
{
// elided
}
}
```
为了避免这种情况,可以修改事件参数,确保取消标志设置后不能被关闭。此外,事件源持有事件订阅者的引用,会导致事件订阅者的对象生命周期与事件源匹配,影响垃圾回收。因此,事件订阅者需要在 `Dispose()` 方法中取消事件处理程序的挂钩。
事件通信虽然能减轻类型之间的静态耦合,但会增加事件生成器和事件订阅者之间的运行时耦合。在设计时,必须考虑这些问题。
#### 3. 虚拟事件的问题
在 C# 中,事件可以声明为虚拟的,但由于事件可以使用字段语法和添加/删除语法声明,实际操作并不简单。创建基类和派生类的事件处理程序时,很容易出现不符合预期的情况,甚至导致难以诊断的崩溃错误。
例如,以下代码中,派生类重写事件会隐藏基类的事件字段,导致基类中引发的事件无法调用订阅者的代码。
```csharp
public class WorkerEngineDerived : WorkerEngineBase
{
protected override void SomeWork()
{
Thread.Sleep(50);
}
// Broken. This hides the private event fie
```
0
0
复制全文
相关推荐










