隐式与显式实现接口

 当我们在使用Visual Studio编写代码实现某个接口的时候,Visual Studio的智能提示就会提示我们要实现接口。点击该提示之后,可以看到两个选项,一个是实现该接口,另一个是显式的实现该接口。接下来,我们看看这两种实现接口的方式有什么不同之处。
当你创建一个类型时,公共语言运行时(Common Language Runtime, CLR)会为该类型创建并初始化一个方法表,在这个方法表中,该类型的所有方法都有一个对应的记录项。它包括类型自身定义的新方法和继承自基类的方法以及实现街口时由接口定义的方法。
比如:
   
public class ArticleService : IArticleService { public int Insert(Article a) { return - 1 ;} } public interface IArticleService { int Insert(Article a); }
接口IArticleService中定义的一个方法 int Insert(Article a); 如上述,上面的代码可以得出,CLR为其创建的方法表应该包含以下记录项:
1、 Object定义的所有虚实例方法。(Object是终极基类,C#中所有的类型都有隐式的继承自Object。)
2、 IArticleService接口定义的所有方法。
3、 ArticleService 类型引入的新方法。上面代码中int Insert(Article a)就是新引入的方法。甚至可以说它并不是在实现接口方法。
      由于类型定义的Insert方法与它所实现的接口定义的方法具有完全相同的方法签名、返回类型以及可访问性,于是CLR便假设这个方法是对接口方法的一个实现。若该方法被标记为virtual ,CLR仍然会如此假设。
      做出这些假设之后,CLR就是会成有关该类型的元数据,指明方法表中的两个方法共享同一个实现。这个两个方法是IArticleService.Insert(Article a)和Insert(Article a),前者为接口定义,后者是类型新引入的方法。在元数据中他们共享同一个实现。
   
public class Program { public static void Main() { ArticleService a = new ArticleService(); Console.WriteLine( a .Insert( new Article())); Console.WriteLine(((IArticleService) a ).Insert( new Article())); } }
这段代码的打印结果是
 
-1 
-1
       可以看出这个两个方法都调用同一个实现。这种实现接口的方式就叫做 隐式的实现接口。
       还有一种实现接口的方式是显示的实现接口(Explicit Interface Method Implementation , EIMI)。现在我们来修改ArticleService对接口的实现。
   
public class ArticleService:IArticleService
{
public int Insert(Article a)
{ return - 1 ; }
int IArticleService.Insert(Article a)
{ return 1 ; }
}
在不改变Main方法的情况下,它的输出将会有所改变: 结果变为
    
       为什么会有这些改变呢?因为显式实现接口之后,CLR为类型生成元数据的时候,前面提到过的方法表中的那两个方法将会指向不同的实现。所以打印结果改变了。显式实现接口时要注意,显式实现接口方法是不能指定其可访问性的,CLR在编译类型的时候会自动给它加上private 。这是为防止类型的实例调用它,事实上它必须是私有的,否则实例调用就会出现混淆。要调用显式实现的接口方法,必须将类型强制转换成接口类型,才能调用这个方法。显式实现接口方法不能被标记为virtual , 所以也就不能被派生类重写,不能被派生类访问,不能被自身实例访问,这看起来都不像是这个类所拥有的方法。它看起来是把类类接口绑定在一起一样。
<think>我们之前已经讨论过实现实现的区别,但用户再次询问核心区别,说明可能需要更精炼的总结。 核心区别在于:实现需要程序员主动声明类型接口的关系(通过特定代码模),而实现只需方法匹配,无需声明。 注意:Go语言本身是实现接口实现是开发者通过代码模(如var _ Interface = (*Type)(nil))来强制检查。 因此,核心区别可归纳为:声明方、验证时机、耦合度、可读性、维护性等。 但用户要求核心区别,所以需要高度概括。</think>### 实现 vs 实现的核心区别(Go接口) 在 Go 语言中,接口实现的根本差异体现在**声明方**和**验证机制**上: | 维度 | 实现 | 实现 | |--------------|-----------------------------------|-----------------------------------| | **声明要求** | 需主动声明类型-接口关系(代码级) | 无需任何声明 | | **验证方** | 编译时通过绑定语句立即验证 | 编译时自动检查方法签名 | | **错误反馈** | 在类型定义处报错(快速定位) | 在使用处报错(延迟发现) | | **代码示例** | `var _ Writer = (*File)(nil)` | 直接赋值:`var w Writer = File{}` | | **设计哲学** | 主动契约设计 | 被动鸭子类型 | | **核心本质** | 开发者添加的**验证约束** | 语言内置的**类型系统特性** | #### 关键区别图解 ```mermaid graph TD A[定义接口] --> B[实现] A --> C[实现] B --> D[主动声明:var _ Interface = Type] B --> E[编译时立即验证] C --> F[仅需方法匹配] C --> G[使用时自动转换] ``` ### 核心区别详解 1. **声明发现的本质差异** - :开发者**主动宣告** "我的类型实现了此接口" - :编译器**被动发现** "这个类型的方法集匹配接口" 2. **错误处理对比** ```go // 实现:修改接口后立即报错 type Storage interface { Save() } type Disk struct{} var _ Storage = (*Disk)(nil) // 编译报错:Disk缺少Save方法 // 实现:错误延迟到使用点 func Store(s Storage) {...} Store(Disk{}) // 调用时才报错 ``` 3. **工程化影响** - 优势: - 接口变更时快速定位所有实现 - 清晰文档化类型职责 - 避免意外实现(如实现敏感接口) - 优势: - 更简洁的代码 - 灵活适配第三方类型 - 减少样板代码 > **Go语言设计者Rob Pike的观点**: > *"实现是Go的根基,它使接口实现解耦。验证是优秀的工程实践,但不是语言特性——它只是把编译器的类型检查提前了。"*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值