空接口有代码味道吗?
我有一个函数返回相同类型的对象(查询结果),但没有共同的属性或方法。为了拥有一个通用类型,我使用一个空接口作为返回类型,并在两者上“实现”它。
这听起来当然不对。我只能通过坚持希望有一天这些类会有一些共同点来安慰自己,我会将这些共同逻辑移到我的空界面中。但我并不满足,并思考是否应该有两种不同的方法并有条件地调用 next。这是一个更好的方法吗?
我还被告知 .NET Framework 使用空接口来进行标记。
我的问题是:空接口是设计问题的强烈标志还是它被广泛使用?
编辑:对于那些感兴趣的人,我后来发现函数式语言中的可区分联合是我想要实现的目标的完美解决方案。 C# 似乎对这个概念还不太友好。
编辑:我写了一篇更长的文章关于这个问题,详细解释了问题和解决方案。
I have a function that returns same kind of objects (query results) but with no properties or methods in common. In order to have a common type I resorted using an empty interface as a return type and "implemented" that on both.
That doesn't sound right of course. I can only console myself by clinging to hope that someday those classes will have something in common and I will move that common logic to my empty interface. Yet I'm not satisfied and thinking about whether I should have two different methods and conditionally call next. Would that be a better approach?
I've been also told that .NET Framework uses empty interfaces for tagging purposes.
My question is: is an empty interface a strong sign of a design problem or is it widely used?
EDIT: For those interested, I later found out that discriminated unions in functional languages are the perfect solution for what I was trying to achieve. C# doesn't seem friendly to that concept yet.
EDIT: I wrote a longer piece about this issue, explaining the issue and the solution in detail.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
尽管该用例似乎存在一种设计模式(现在很多人都提到了“标记接口”),但我相信这种做法的使用表明了代码味道(至少在大多数时候)。
正如 @V4Vendetta 发布的,有一个针对此的静态分析规则:
http://msdn.microsoft.com/en-我们/库/ms182128(v=VS.100).aspx
这是引用的 MSDN 建议:
这也反映了已发布的维基百科链接的批评部分。
Although it seems there exists a design pattern (a lot have mentioned "marker interface" now) for that use case, i believe that the usage of such a practice is an indication of a code smell (most of the time at least).
As @V4Vendetta posted, there is a static analysis rule that targets this:
http://msdn.microsoft.com/en-us/library/ms182128(v=VS.100).aspx
This is the quoted MSDN recommendation:
This also reflects the Critique section of the already posted wikipedia link.
您声明您的函数“根据某些情况返回完全不同的对象” - 但它们到底有多大不同?一个可以是流编写器,另一个可以是 UI 类,另一个可以是数据对象?不……我怀疑!
您的对象可能没有任何通用的方法或属性,但是,它们的角色或用法可能相似。在这种情况下,标记接口似乎完全合适。
You state that your function "returns entirely different objects based on certain cases" - but just how different are they? Could one be a stream writer, another a UI class, another a data object? No ... I doubt it!
Your objects might not have any common methods or properties, however, they are probably alike in their role or usage. In that case, a marker interface seems entirely appropriate.
如果不用作标记接口,我会说是的,这是代码味道。
接口定义了实现者遵守的契约 - 如果您有不使用反射的空接口(就像标记接口那样),那么您不妨使用
Object
作为 (已经存在)基本类型。If not used as a marker interface, I would say that yes, this is a code smell.
An interface defines a contract that the implementer adheres to - if you have empty interfaces that you don't use reflection over (as one does with marker interfaces), then you might as well use
Object
as the (already existing) base type.你回答了你自己的问题...“我有一个函数,根据某些情况返回完全不同的对象。”...为什么你想要有一个返回完全不同对象的相同函数?我看不出它有什么用处,也许你有一个很好的,在这种情况下,请分享。
编辑:考虑到您的澄清,您确实应该使用标记接口。 “完全不同”与“同类”有很大不同。如果它们完全不同(不仅仅是它们没有共享成员),那就会产生代码味道。
You answered your own question... "I have a function that returns entirely different objects based on certain cases."... Why would you want to have the same function that returns completely different objects? I can't see a reason for this to be useful, maybe you have a good one, in which case, please share.
EDIT: Considering your clarification, you should indeed use a marker interface. "completely different" is quite different than "are the same kind". If they were completely different (not just that they don't have shared members), that would be a code smell.
正如许多人可能已经说过的那样,空接口确实可以有效地用作“标记接口”。
我能想到的最好的用途可能是将对象表示为属于域的特定子集,由相应的存储库处理。假设您有不同的数据库来检索数据,并且每个数据库都有一个存储库实现。特定存储库只能处理一个子集,并且不应为任何其他子集提供对象的实例。您的域模型可能如下所示:
然后,您的存储库可以通用到 ISecurityDomainObject、INorthwindDomainObject 和 ISouthwindDomainObject,然后您可以在编译时检查代码是否试图将 Security 对象传递给 Northwind DB(或任何其他排列)。在这种情况下,接口提供了有关类性质的有价值的信息,即使它不提供任何实现契约。
As many have probably already said, an empty interface does have valid use as a "marker interface".
Probably the best use I can think of is to denote an object as belonging to a particular subset of the domain, handled by a corresponding Repository. Say you have different databases from which you retrieve data, and you have a Repository implementation for each. A particular Repository can only handle one subset, and should not be given an instance of an object from any other subset. Your domain model might look like this:
Your repositories can then be made generic to ISecurityDomainObject, INorthwindDomainObject, and ISouthwindDomainObject, and you then have a compile-time check that your code isn't trying to pass a Security object to the Northwind DB (or any other permutation). In situations like this, the interface provides valuable information regarding the nature of the class even if it does not provide any implementation contract.