【问题标题】:Why prefix C# interface names with an “I”为什么在 C# 接口名称前加上“I”
【发布时间】:2009-01-13 01:03:03
【问题描述】:

这种命名约定背后的基本原理是什么?

我没有看到任何好处。额外的前缀只会污染 API。

我的想法与康拉德的response 与此相关的question 一致;选择的answer 主要是我在这里要求的。

【问题讨论】:

  • 我看到两票赞成关闭——但我不相信这真的与替代请求完全重复。如果有人能说服我,我会投票关闭。 (请发表评论。)
  • 不重复,实际上是一个好问题。
  • 不是重复的,但 Jon Limjap 似乎不这么认为。我能做什么?
  • 不仅仅是 Jon Limjap。 3个人必须投票,他恰好是第3个人投票。
  • @Simucal - 感谢您的澄清。是否可以投票取消关闭它?如果选民提供了假定副本的链接,将会很有帮助。

标签: c# naming-conventions interface


【解决方案1】:

完全相反,命名约定清楚地标识了一个接口。

例如,如果您有:

public class Dog : IPet, IMammal
{
    ....

仅仅通过阅读,我可以有把握地假设 IPet 和 IMammal 可能是接口。

.NET CLR 允许单类继承。所以,如果我有一个基类..我只能从它继承一个类。让我们将 IPet 接口更改为基类。我们的示例现在变为

public class Dog : Pet, IMammal
{
    ....

我从 Pet 类继承并实现 IMammal 接口。

如果我们按照您的建议进行操作并删除了字母“I”,我们将得到:

public class Dog : Pet, Mammal
{
    ....

我从哪个类继承?我正在实现哪个接口?它会变得混乱吗? (仅供参考..您应该始终将基类放在首位,因此您可以争论这一点...但是如果您主张从接口名称前缀中删除字母 I,我怀疑您是否也遵循这种做法)

正如您所见,命名约定很容易告诉我很多关于我的对象的信息,而无需我进一步调查。我可以很容易地看到我正在继承什么与我正在实施什么。

【讨论】:

  • 有道理。那么,在 C# 中,Java 的“扩展和实现”是混在一起的?
  • 我遵循你提到的“基类总是第一”的约定。尽管如此,我不同意您的说法“命名约定很容易告诉我很多关于我的对象的信息”。举个例子,在 API 级别区分接口、抽象/具体类、枚举、struts 等对我来说没有意义。
  • 为什么我们不在抽象/具体类型前加上“A”/“C”,枚举加上“E”,结构加上“S”等等?
  • 颇具讽刺意味的是,java 实际上做对了。接口不再以“I”为前缀,但实际上它被认为是不好的做法。请参阅 Joshua Bloch 的“Effective Java”。
  • 这没有解决一个更基本的问题,即为什么需要将类型标识为接口,而不是需要确定它是类还是结构。 ..或者它是否是抽象的......,或者它是否包含实例成员或仅包含静态成员......我认为我们这样做,但我不确定最好的答案是什么...... .
【解决方案2】:

我也喜欢它,因为我可以将它读作“Iverb-behavior”,如“ICanSave”或“IDoDoubleEntry”等......

【讨论】:

  • 也许前缀应该是“我”。
  • 是的,可以说“Am”是隐含的。“IAmEnumerable”可以说会更清晰,但时间更长。
  • 我总是觉得我的接口名称听起来有点像笑猫。 “ICanHasCheeseburger”
  • 这实际上不违反 C# 的命名约定吗? ICanSave 应该是 ISaveble
  • @CharlesBretana 我只是在想,因为 .NET 将它们的接口命名为 IClonableIDisposableIComparable 而不是 ICanCloneICanDisposeICanCompare。跨度>
【解决方案3】:

我认为 IInterface 命名约定很愚蠢。这是匈牙利符号的一个例子,我赞同鄙视匈牙利符号的思想流派。如果您的接口只有一个同名实现,请考虑这可能是代码异味。

不过,我还是用它,因为在这种情况下,IInterface 是微软推荐的,“标准胜于优”。

【讨论】:

  • 我也从来不是匈牙利语的粉丝,但我最近看到了 Joel 的这篇旧文章,它解释了它的起源以及它是如何被广泛误解的——至少我现在明白了它背后的原因! joelonsoftware.com/articles/Wrong.html
【解决方案4】:

为什么这不是句法突出显示而不是匈牙利符号的功能?如果区分类和接口非常重要,为什么 IDE 不将引用接口的标识符用斜体表示。我讨厌在字段前放置“”或“m”,在类前放置“C”等。更糟糕的是,它鼓励程序员编写非常糟糕的 API,例如:

public class List : IList

而不是更合理的:

public class LinkedList : List
public class ArrayList : List
public class HashList : List

即使是 .NET 通用类的作者也落入了这个陷阱。类名永远不应该是只删除“I”的接口名称。类名应始终告诉用户该类与接口的其他可能实现有何不同。仅出于这个原因,我投票赞成放弃愚蠢的“我”。

另外,当我使用智能感知时,我想按功能区域对事物进行分组,而不是按类或接口进行分组。我从来没有想过,“我需要一个接口,而不是一个类。”我总是想,“我需要做 X 的东西”。

【讨论】:

  • 我很高兴不是唯一一个这样看的人。看来我确实是少数。 4 年后,我仍然惊讶于有多少读者发誓“它使识别界面变得容易”而完全没有抓住重点。
  • 在设计具有可扩展性的接口时,这是一个非常好的观点,但抽象接口(C# 和 Java)的另一个用例纯粹是为了解耦以允许模拟依赖项,其中没有该接口的其他具体实现已明确“设计用于”。所以MyReallySpecificBusinessComponentIMyReallySpecificBusinessComponent 就可以了。但绝对不是List
【解决方案5】:

实际上我发现避免命名冲突很有用,例如,我可能会创建一个名为 Fred 的具体类来实现 IFred

【讨论】:

  • 使用有意义/有用的名称可以避免命名冲突。例如。 Fred 扩展了 Person,实现了 Living、Breathing 和其他功能。如果没有更多上下文,我不能肯定地说,但如果 Fred 是一个域/模型对象,那么拥有它的接口可能是多余的。
【解决方案6】:

我一直认为将动词用于行为界面很有趣。这与使用名词的类命名约定背道而驰,但它允许类“说出”它的行为。

class Dog: IBark

这对于像 WCF 接口这样的结构化接口并不适用,但我们不需要一直玩得开心。

要回答您的问题,请将I 视为“实现”所以...

class DogDataService : Dog, IDataService

这个服务类继承自Dog并实现IDataService

我还没有真正回答你的问题,但I 很有用,因为你会在命名空间、类和接口之间遇到命名冲突。

namespace DataService
interface DataService
class DataService: DataService

所以我们结束了

namespace DataServices
interface IDataService
class DataService : IDataService

我认为实际上,这是一个理智的约定。

【讨论】:

  • +1 - 一般来说,我认为命名约定表明开发语言存在弱点(这里可能就是这种情况),但是当你想要避免这些冲突时,能够避免这些冲突仍然很重要使用依赖注入并模拟合作者对象。当您为域对象创建接口时,除了命名约定之外确实没有其他选择。
  • /shrug 我也将它用于 WCF 服务。我的两项服务有 IServeData 和 ICanMakePayments。
  • @JeffSternal 我认为您可能正在应用“每个对象都必须有一个接口”反模式。您可能不应该为域对象创建接口;它们通常是您的公共 API 的预期部分。
【解决方案7】:

如果您考虑两个“最佳实践格言”

清晰为王

噪音不好

它们之间存在冲突。问题是:什么时候清晰变成噪音?

对我来说,写Person person = new PersonImpl() 比写IPerson person = new Person() 更嘈杂(但同样清晰)。

【讨论】:

  • 或者更好的是,Person p = new TallPerson();但就我而言, Person 不太可能是一个接口。相反,它是 TallPerson 扩展的基本类型或抽象类型。在任何一种情况下,Person 的“接口”都隐含在其公共 API 中。我和 IFoo 一样鄙视 FooImpl()。
【解决方案8】:

要么就是这样,要么在接口的实现中添加“Impl”(啊)。我对“我”没有意见,它是对接口的最简单直接的命名。

【讨论】:

  • 我不同意“Impl”的建议——它会像“I”前缀一样浪费噪音。命名实现时应牢记其实现。例如,可以使用“RelationalDatabase”和“InMemoryDatabase”来实现接口“Database”。
  • 这是一个约定,如果你不喜欢它,你不需要遵守它。
  • 不是我喜欢的问题。我质疑它的价值。我喜欢遵守约定,不仅仅是为了跟风。我想了解理性。希望有人能在“它让你知道它是一个界面”(IDE 用漂亮的图标做到这一点)之外表达这种理性。
【解决方案9】:

“I”约定似乎是一个与今天无关的旧约定。当前的代码编辑器提供了有关您正在使用的类型的大量见解,因此认为更容易识别接口就像要求命名空间以“N”为前缀,因为您想确保不会混淆它一个具体的类(带有“C”的前缀?)。

约定并不意味着它是一个好的约定。有时,只是因为人们开始使用它......

以 C# 文档生成器为例:它不关心它...如果您的界面没有以“I”为前缀,您仍然会在文档的界面部分看到您的界面。您真的认为在文档的接口部分中为所有接口添加前缀“I”是相关信息并帮助您更好地识别接口吗?

【讨论】:

    【解决方案10】:

    需要区分接口和类实际上表明存在设计缺陷。在设计良好的应用程序中,它总是很清楚。一个子类应该始终是一个专业化,并且类只能专注于一个学科,不能更多。

    一个类应该有一个存在的理由。永远不应要求将次要角色放在基类中。例如:

    public class XmlConfigurationFile : ConfigurationFile, IDisposable
    {
    }
    
    public class YamlConfigurationFile : ConfigurationFile, IDisposable
    {
    }
    

    第一个是Xml专用的配置文件,第二个是Yaml专用的配置文件。这些也是一次性的,但这并不重要。由于处理过程不同,您没有创建这两个类。

    对比一下:

    public class XmlConfigurationFile : IDisposable, ConfigurationFile
    {
    }
    

    这将告诉您 XmlConfigurationFile 的主要目的是它是一次性的。您可以将其用作表示配置文件的一种方式,这很好,但只是次要的。

    当您创建具有多种存在原因的类时,问题就开始了:

    public class MyConfigurationFile : XmlConfigurationFile, YamlConfigurationFile
    {
    }
    

    即使 XmlConfigurationFile 和 YamlConfigurationFile 本来是接口,它仍然表明设计不好。你的配置文件怎么能同时是Xml和Yaml呢?

    如果您仔细阅读给出的示例(此处和其他地方),人们总是很难找到 I 前缀何时重要的好示例。这里的答案之一是:

    public class Dog : Pet, Mammal
    {
    }
    

    这就是这个类在一个关于宠物的应用程序中的样子。狗的主要目的是成为专门的宠物,可以做与宠物相关的事情,而不是哺乳动物。

    public class Dog : Mammal, Pet
    {
    }
    

    这是同一类在有关动物分类的应用程序中的外观。很高兴知道狗是宠物,但它的主要目的是成为一种专门的哺乳动物,可以做与哺乳动物相关的事情。

    我认为您的课程应该告诉您有关应用程序架构和领域的正确故事。要求接口以“I”为前缀是一项技术要求,并不能帮助您更好地讲述应用程序的故事。

    一旦您开始编写小型、专用、单一用途的类,了解它是实现还是扩展的需求将自动消失。

    【讨论】:

    • 这必须是经过批准的答案。写得很好,内容丰富。谢谢。
    【解决方案11】:

    它使它很容易被识别为一个接口。

    【讨论】:

    • 我认为区分接口和实现没有任何直接价值。进一步阐述可能会有所帮助。
    • 对我来说,至少,Implementation 意味着一个类,应该根据逻辑实体/对象语义来定义,而 Interface 意味着行为或契约,应该根据连接行为的逻辑分组来组织.
    【解决方案12】:

    命名约定的好处是在您使用对象之前告诉您一些有关该对象的信息。命名约定已被广泛使用多年,一直追溯到 fortran 坚持整数值被限制(如果我没记错的话)为变量名称,如“i”和“j”。

    匈牙利符号将命名约定提升到了一个全新的丑陋水平,它描述了变量类型,无论它是否是指针等。我们中的许多人接触过大量使用匈牙利符号的代码后都会出现紧张的抽搐和口吃。

    在接口名称前加上 I 是一种影响相对较小、无害的识别对象的方法。

    【讨论】:

    • 识别类型是接口如何使 API 受益? (我确实理解接口和类之间的区别)。
    【解决方案13】:

    TL;DR - 从 class Foo 中提取 interface IFoo 在 SOLID 解耦中很常见,尤其是用于单元测试目的

    对我来说,类 Foo 实现接口 IFoo 的双重约定(特别是如果两者都在同一个程序集中)传达了一个特定的意图:

    • 将依赖项耦合到 Foo 应该始终是间接的,通过相应的 IFoo 接口(并且可能通过 IoC 容器注入)
    • IFoo 的初始设计是一个专有的、不可重用的接口,专门允许依赖于Foo 的类在单元测试期间模拟这种依赖关系。
    • 除此之外,读者无需在IFoo 接口的设计中推断出任何额外的智能
    • 相反,如果以后需要IFoo 的多个具体实现类,则需要将适当的接口隔离设计改进到层次结构中。

    基本原理

    为了能够模拟或存根一个类,单元测试中广泛接受的最佳实践是仅通过接口解耦类之间的依赖关系。这种接口解耦也将在其他情况下 never had a design requirement for polymorphicism 的类上进行(即,如果不需要单元测试,则只会存在一个这样的实现)。

    因此,这些接口(例如Interface Segregation Principal of SOLID)的重构和重用并不经常应用于此类“可模拟”接口——公共方法、属性和事件之间通常存在 1:1 的相关性一个“可模拟”类 (Foo) 及其解耦接口 IFoo(类似于 COM 时代的 automatic interfaces in VB)。

    VS 和 Resharper 等工具可以轻松地将此类公共符号从类中提取到单独的接口中,作为事后的想法。

    此外,如果我们考虑到像 Moq 这样的 Mocking 框架允许 definition of implementations of the interface on-the-fly,我们不必浪费精力来命名具体的测试双重实现类。

    【讨论】:

      【解决方案14】:

      这只是一个命名约定,所以每个人都会知道它是接口还是其他东西,它不是强制性的,也不是编译器或 IDE 的,但我一生中看到的所有接口都以字母 I 开头

      【讨论】:

        【解决方案15】:

        我似乎是匈牙利符号的传统约定。 Interface Naming Guidelines 表示“在接口名称前加上字母 I,表示该类型是一个接口。” Framework Design Guidelines 还说“DO 用字母 I 作为接口名称的前缀,表示该类型是一个接口。”

        这只是一个编码约定,因此很难确定好坏。 重要的是一致性。

        【讨论】:

          【解决方案16】:

          首先我认为前缀 I then description 是错误的,因为这意味着实现可以有一个更短的名称。 IList (intf) -> 列表。这是一种反模式,因为我们都知道我们应该在创建时使用 intf 并且可能只使用具体类型。不要激怒我,这是一个概括,但前提是 intf 很少 impl 。实现名称应该描述它如何实现 intf 或它在做什么。想想intf List,LinkedList,它使用链表实现List。谁在乎它是否更长,因为我们大部分时间都应该使用 List 。如果我们有一个实现许多 intf 的类,我们可能不应该包含所有 intf 作为类的真正目的的影子。在没有 intf 的情况下删除的东西是有意义的。例如,人们用我的名字叫我而不是人、兄弟、开发人员等是最好的最具描述性的名字。我想如果一个类是一个简单的 intf 然后调用它 Default Intf 这使得它在 ious 这是 Intf 的默认实现。 类的名称最终应该是人类可读的,并且几乎是描述其目的的简短短语。前缀代码等并不是很好,因为我们用文字而不是代码进行交流。计算机并不关心类的名称,所以剩下的就是我们为事物命名,以便这些名称对我们和我们的同事有所帮助。

          【讨论】:

            【解决方案17】:

            最有可能让它在智能感知中易于识别,因为所有接口都会聚集在一起。类似于我为所有 UI 控件添加 btn、tb、lb 前缀的方式。当智能感知启动时,所有内容都聚集在一个简单的组中。

            【讨论】:

            • 我相信 IDE 可以在不使用命名约定的情况下“聚集”。
            【解决方案18】:

            有了关于命名约定的所有论点,并为变量和方法赋予正确的名称,这些变量和方法实际上描述了它们的作用……为什么不直接命名你的接口(例如 PetInterface、PlayerInterface 等)并取消前缀“我”都在一起。所以你必须输入额外的 9 个字母,至少“I”被删除,我们知道它不是一个类,因为它说“接口”。

            【讨论】:

            • 这是个糟糕的主意。为什么不将您的课程命名为“DogClass”?如果有人在我的代码库中这样做,我会非常沮丧。有标准;使用它。
            猜你喜欢
            • 2011-08-14
            • 1970-01-01
            • 2019-06-05
            • 2012-01-04
            • 2011-08-22
            • 1970-01-01
            • 2019-05-16
            相关资源
            最近更新 更多