在 IL 中隐藏接口的公共成员

Posted

技术标签:

【中文标题】在 IL 中隐藏接口的公共成员【英文标题】:Hiding public members of an interface in IL 【发布时间】:2015-06-20 16:09:46 【问题描述】:

考虑这些代码行:

ConcurrentDictionary<string, object> error = new ConcurrentDictionary<string, object>();
error.Add("hello", "world"); //actually throws a compiler error

IDictionary<string, object> noError = new ConcurrentDictionary<string, object>();
noError.Add("hello", "world");

我最终发现您所要做的就是更改 IL 以将 Add 函数设为私有。

现在本着解耦代码的精神,我很可能会使用接口,但似乎在 Add 方法中找不到并发字典。

真正使用Add是否安全(我无法查看IL,所以我不知道它是否真的是线程安全的。)?或者我应该使用ConcurrentDictionary&lt;TKey, TValue&gt; 的具体类型并明确使用TryAdd

【问题讨论】:

您的标题和实际问题并不相符。 There is no such thing as a non-public member of an interface. @ErikPhilips 不确定链接的相关性。很明显 IDictionary 暴露了 Add,但 Concurrent Dictionary 没有,这意味着它确实实现了接口但功能仍然隐藏。 它没有隐藏。您需要了解Implicit Implementation versus an Explicit Implementation 之间的区别。在任何一种情况下,Add() 都已实现并公开。 @evanmcdonnal,但它确实存在,它是 documented by microsoft。你有看过吗? @evanmcdonnal 它在你给 LOL 的 href 中列出,它在 Explicit Interface Implementations 下。你真有趣。 【参考方案1】:

是的,很安全。

看看reference source for ConcurrentDictionary。方法IDictionary&lt;TKey, TValue&gt;.Add 只是调用TryAdd,如果key 已经存在则抛出异常。

接口成员的隐藏不需要进行 IL 修改。可以通过 C# 中的explicit interface implementation 来完成。这是通过去掉方法的访问修饰符并在方法名称前加上接口名称来完成的:

void IDictionary<TKey,TValue>.Add(TKey key, TValue value) 

这样做有多种原因,也许你不想弄乱具体的接口,或者你希望你的类的消费者明确说明他们正在使用什么方法,如果接口上的方法名称不是'足够具体。此外,它还允许您为具有相同签名的不同接口上的方法提供单独的实现(我认为ConcurrentDictionary 不是真正的问题,但如果您在自己的类中需要它,该功能就在那里)。

【讨论】:

我猜这是为了让你可以对某人说,“如果你知道你正在使用 ConcurrentDictionary,你应该使用 TryAdd”,但在访问时它仍然表现得像 IDictionary通过界面。 所以人们会明白,如果你使用具体版本,你的代码应该是可读的并使用TryAdd,具体说明你的代码是做什么的。 我认为这个讨论是相关的...***.com/questions/143405/…【参考方案2】:

Hmya,你在玩一个危险的游戏。 ConcurrentDictionary 类的 public 接口提供线程安全的方法,您可以在调用时感觉良好,知道它们表现良好。

但是 Add() 方法不是这样的方法,只有 TryAdd() 是。您会收到编译错误,因为 Add() 不是公共的,Microsoft 有意通过编写显式接口实现版本使该方法无法访问。否则他们必须做的事情,ConcurrentDictionary 实现 IDictionary。他们是否应该实现了这个接口是有争议的。但是桥上的水,他们做到了。

当然,您可以非常轻松地转换为访问 Add()。但现在这种感觉良好的感觉开始在边缘发展。非常恰当地,他们可以实现 Add() 的唯一方法是让它在 TryAdd() 失败时抛出异常。带有 ArgumentException 的 Kaboom,祝您调试顺利。

当这样的基本操作失败时,您是否绝对 100% 确定可以处理工作线程上的异常?您是否考虑过在 catch 子句中需要做什么,而不是考虑入侵 IL?如果你这样做了,当 TryAdd() 返回 false 时,它​​与你编写的代码到底有什么不同?

没有什么不同。

【讨论】:

这并没有在生产代码中实现,更多的是修补。我注意到了这种情况,并出于您指定的确切原因决定只使用具体类型。我只是觉得有点奇怪,觉得值得一问。 嗯,一个令人讨厌的不可调试的异常被赞成作为一个特性。伙计们,我们在这里做什么?保持祈祷不会发生?

以上是关于在 IL 中隐藏接口的公共成员的主要内容,如果未能解决你的问题,请参考以下文章

封装,继承,多态,接口

新的静态字段并隐藏公共继承成员

如何从 IntelliSense 隐藏公共方法

Java封装 概述

静态库:从头文件中隐藏私有成员

JAVA-初步认识-第六章-封装思想