Effective C# Item 20: Distinguish Between Implementing Interfaces and Overriding Virtual Functions
乍看之下,实现接口和重写虚方法是相似的,它们都为一个声明好的成员提供了定义。这种看法是错误的。实现接口和重写虚方法是有很大区别的。在默认情况下,在接口中声明的成员不是虚拟的。派生类不能够重写基类中实现的接口。
有一种方法可以让派生类修改基类中接口的实现。要实现这一点,我们需要为派生类创建一个钩子(hook)。
下面我们举一个例子,一个简单的接口和一个类对接口的实现如下:











Message()方法是MyClass类的公共接口中的一部分。我们也可以通过IMsg接口来访问MyClass类型中的成员。现在我门来让派生类中的情况稍微复杂一些:







注意到我们在这里使用了new关键字来定义之前已经存在的Message()方法。MyClass.Message()方法不是虚方法,派生类是不能对它进行重写的。这里派生类创建了一个新的Message(),而不是重写了基类中的方法。因此,我们还是可以通过IMsg接口来访问到基类中的Message()方法。





接口方法不是虚拟的,当我们实现接口时,就是为我们的类声明了一个有特殊含义的具体实现。
但是有时候我们希望创建接口,在基类中实现,并且在派生类中修改它们的行为。这是可以做到的。我们有两种选择,如果我们的基类是不可达的,那么我们可以在派生类中重新实现该接口:







额外添加的IMsg改变了我们派生类的行为。现在IMsg.Message()使用的将是派生类中实现的版本。





我们依然需要使用new关键字来声明MyDerivedClass.Message()方法。但是这仍然存在问题。通过基类的引用我们仍然会得到基类中的实现版本。
解决这个问题的唯一方法是修改基类,将这个接口方法声明为虚拟的。







这样所有从MyClass派生的类都可以声明它们自己的Message()了。不论是通过派生类,接口还是基类,调用的始终会是重写的版本。
如果我们不喜欢这种混乱的虚拟函数,可以稍微对基类作些修改:




现在我们可以实现一个接口而不完全实现其中的方法。因为将方法声明为抽象的,我们必须在派生类中提供它的具体实现。IMsg是MyClass声明中的一部分,但是其中方法的具体定义却转移到派生类中实现。
实现接口比创建和重写虚方法有更多的选择。我们可以将实现声明为密封的,虚拟的,或者抽象的。我们可以根据具体情况来决定派生类是否可以修改基类中对接口的默认实现。
译自 Effective C#:50 Specific Ways to Improve Your C# Bill Wagner著
回到目录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通