是否需要XML注释文档?


10

我曾经是要求XML注释用于文档的爱好者。从那以后,我改变了主意,主要有两个原因:

  1. 像好的代码一样,方法应该是不言自明的。
  2. 实际上,大多数XML注释都是无用的噪声,不会提供任何附加值。

很多时候,我们只是使用GhostDoc生成通用注释,这就是我所说的无用噪声:

    /// <summary>
    /// Gets or sets the unit of measure.
    /// </summary>
    /// <value>
    /// The unit of measure.
    /// </value>
    public string UnitOfMeasure { get; set; }

对我来说,这很明显。话虽如此,如果要包含特殊说明,那么我们绝对应该使用XML注释。

我喜欢这篇文章的摘录:

有时,您将需要写评论。但是,这应该是例外而不是规则。仅当注释表示无法用代码表达的内容时,才应使用注释。如果要编写简洁的代码,请努力消除注释,而改写自记录代码。

我认为我们仅应在代码不足以自行解释时使用XML注释吗?

我相信这是一个很好的示例,其中XML注释使漂亮的代码看起来很难看。需要上这样的课...

public class RawMaterialLabel : EntityBase
{
    public long     Id                      { get; set; }
    public string   ManufacturerId          { get; set; }
    public string   PartNumber              { get; set; }
    public string   Quantity                { get; set; }
    public string   UnitOfMeasure           { get; set; }
    public string   LotNumber               { get; set; }
    public string   SublotNumber            { get; set; }
    public int      LabelSerialNumber       { get; set; }
    public string   PurchaseOrderNumber     { get; set; }
    public string   PurchaseOrderLineNumber { get; set; }
    public DateTime ManufacturingDate       { get; set; }
    public string   LastModifiedUser        { get; set; }
    public DateTime LastModifiedTime        { get; set; }
    public Binary   VersionNumber           { get; set; }

    public ICollection<LotEquipmentScan> LotEquipmentScans { get; private set; }
}

...并变成这样:

/// <summary>
/// Container for properties of a raw material label
/// </summary>
public class RawMaterialLabel : EntityBase
{
    /// <summary>
    /// Gets or sets the id.
    /// </summary>
    /// <value>
    /// The id.
    /// </value>
    public long Id { get; set; }

    /// <summary>
    /// Gets or sets the manufacturer id.
    /// </summary>
    /// <value>
    /// The manufacturer id.
    /// </value>
    public string ManufacturerId { get; set; }

    /// <summary>
    /// Gets or sets the part number.
    /// </summary>
    /// <value>
    /// The part number.
    /// </value>
    public string PartNumber { get; set; }

    /// <summary>
    /// Gets or sets the quantity.
    /// </summary>
    /// <value>
    /// The quantity.
    /// </value>
    public string Quantity { get; set; }

    /// <summary>
    /// Gets or sets the unit of measure.
    /// </summary>
    /// <value>
    /// The unit of measure.
    /// </value>
    public string UnitOfMeasure { get; set; }

    /// <summary>
    /// Gets or sets the lot number.
    /// </summary>
    /// <value>
    /// The lot number.
    /// </value>
    public string LotNumber { get; set; }

    /// <summary>
    /// Gets or sets the sublot number.
    /// </summary>
    /// <value>
    /// The sublot number.
    /// </value>
    public string SublotNumber { get; set; }

    /// <summary>
    /// Gets or sets the label serial number.
    /// </summary>
    /// <value>
    /// The label serial number.
    /// </value>
    public int LabelSerialNumber { get; set; }

    /// <summary>
    /// Gets or sets the purchase order number.
    /// </summary>
    /// <value>
    /// The purchase order number.
    /// </value>
    public string PurchaseOrderNumber { get; set; }

    /// <summary>
    /// Gets or sets the purchase order line number.
    /// </summary>
    /// <value>
    /// The purchase order line number.
    /// </value>
    public string PurchaseOrderLineNumber { get; set; }

    /// <summary>
    /// Gets or sets the manufacturing date.
    /// </summary>
    /// <value>
    /// The manufacturing date.
    /// </value>
    public DateTime ManufacturingDate { get; set; }

    /// <summary>
    /// Gets or sets the last modified user.
    /// </summary>
    /// <value>
    /// The last modified user.
    /// </value>
    public string LastModifiedUser { get; set; }

    /// <summary>
    /// Gets or sets the last modified time.
    /// </summary>
    /// <value>
    /// The last modified time.
    /// </value>
    public DateTime LastModifiedTime { get; set; }

    /// <summary>
    /// Gets or sets the version number.
    /// </summary>
    /// <value>
    /// The version number.
    /// </value>
    public Binary VersionNumber { get; set; }

    /// <summary>
    /// Gets the lot equipment scans.
    /// </summary>
    /// <value>
    /// The lot equipment scans.
    /// </value>
    public ICollection<LotEquipmentScan> LotEquipmentScans { get; private set; }
}

2
我认为XML是用于此目的的糟糕选择。对于手边的使用来说,它太冗长和笼统。请检出reStructuredTextsphinx,以了解一种嵌入到注释中而不会使它们难以阅读的标记语言。
Latty

2
@Lattyware:默认情况下,VisualStudio支持此样式,无需额外的插件或工具。以这种方式生成的注释将在弹出的工具提示中立即显示。
FrustratedWithFormsDesigner 2012年

@FrustratedWithFormsDesigner我会说,获得一个插件值得使您的代码更具可读性。PyCharm中的工具提示内置了对reST的支持,因此,我确定其他IDE中的其他语言都存在插件。显然,如果您有一个以这种方式记录所有内容的项目,我并不是建议您开始拆分完成的方式,但是对于新项目,我只是认为阅读和维护它是如此可怕。
Latty 2012年

Answers:


21

如果您的评论仅如下所示:

/// <summary>
/// Gets or sets the sublot number.
/// </summary>
/// <value>
/// The sublot number.
/// </value>

是的,它们并不是那么有用。如果他们读到这样的内容:

/// <summary>
/// Gets or sets the sublot number.
/// Note that the sublot number is only used by the legacy inventory system.
/// Latest version of the online inventory system does not use this, so you can leave it null. 
/// Some vendors require it but if you don't set it they'll send a request for it specifically.
/// </summary>
/// <value>
/// The sublot number.
/// </value>

那我说他们有价值。因此,回答您的问题:当注释说出代码未说的内容时,注释是必需的。

有一个例外:如果您正在编写将对公众开放的库/ API,则最好对可以公开访问的任何内容发表评论。我讨厌使用一个库,并且看到一个getAPCDGFSocket()没有解释APCDGFSocket是什么的命名函数(我对诸如这样的简单操作很满意This gets the Async Process Coordinator Data Generator File Socket)。因此,在这种情况下,我会说使用某种工具来生成所有注释,然后手动调整需要它的注释(并请确保已解释了您的隐喻缩写)。

同样,获取器/设置器通常是“是否需要评论?”的坏例子。因为它们通常很明显并且不需要注释。注释对于执行某种算法的函数更为重要,这些注释以某种方式解释了为什么要做事情,这可以使代码更易于理解,也使以后的程序员更容易使用。

...最后,我非常确定这个问题与所有样式的注释有关,而不仅仅是使用XML格式化的注释(因为在.NET环境中工作,所以使用了注释)。


2
+1-GhostDoc是我将第一个版本(即样板版)转换为第二个版本(包含详细的领域知识)的起点。
Jesse C. Slicer 2012年

4
为何部分+1 。DRY原则适用-不要重复自己,并且如果代码已经很好地描述了什么部分,则您的注释应集中于解释其他内容(通常是为什么)。
丹尼尔·B

@DanielB,或者您根本不需要注释;)我大部分都同意该答案,除了“注释中的某些内容,如果它们说了一些代码未说的内容,则是必需的”。我认为,如果代码说明了所需的一切,那么即使注释中提供的信息不在代码中,您也不需要注释中的更多信息。
吉米·霍法

1
@DanielB-.NET中的XML注释主要用于以下情况:库或服务的最终用户程序员没有可用的源代码。
jfrankcarr 2012年

2
@Lattyware-XML注释与Visual Studio的Intellisense无缝集成,与在单独的文档中查找内容相比,它可以节省大量时间。
jfrankcarr 2012年

5

对于那些无法阅读源代码的用户来说,这些注释看起来毫无用处。当组织外部的人员将该类用作外部API时,就会发生这种情况:从XML文档生成的HTML是了解类的唯一方法。

就是说,重申该方法名称并在单词之间添加空格的注释仍然没有用。如果您的班级将在组织外部使用,则需要至少记录下来您值的有效范围。例如,你应该说,设定UnitOfMeasurenull是非法的,是提供给二传手的价值不能在开头或字符串的结尾包含空格,等等。LabelSerialNumber如果与普通格式不同,还应记录以下范围Int32:也许不允许负数*,或不允许超过七个数字。您的内部用户可能会认为这是理所当然的,因为他们日复一日地查看序列号,但是外部用户可能真的很惊讶地看到一个看起来像无辜的二传手的例外。


* ...在这种情况下uint可能是更好的选择


1
这不仅适用于没有来源的情况。如果您的编辑器可以解析它们(就像Visual Studio使用Xml注释一样),则它们可以将信息作为鼠标悬停/弹出窗口提供,而无需您导航到其他文件。当您转到实现setter的文件时,很明显有一个1行范围验证器将int限制在较窄的范围内。但是当您开始输入“ myFrobable.Fro ...”时,会出现“ FrobableID必须介于0到1000之间”的提示,并且自动填充功能会提醒我们。
Dan在火光中摆弄

1

避免这种无用的评论是绝对正确的。它们使读取代码变得更加困难,而不是使代码变得更容易,并且占用了太多空间。

在我的实践中,使用getter / setter编写注释的人倾向于在确实需要注释时省略注释(例如为没有文档的组件构建20行sql-query)。

当有其他明显的解决方案时,我会写评论_我指出了为什么确实使用了这种方法。或者,如果在不了解所有细节的情况下很难理解这个想法,我将简要列出理解代码所必需的细节。

您带来的示例更多是在写评论,说一个人写评论而不是使他人(以及他们的)生活更轻松。

顺便说一句,您可以通过返回原来的代码并尝试理解它来提高编写注释的能力(您甚至可能在2-3个月内无法识别您自己的代码,这绝对就像阅读别人的代码一样)。如果您毫不费力地执行此操作,那一切就好了。


我不知道再有谁会努力对吸气剂/吸气剂发表评论。如果您使用的是几乎所有现代IDE(甚至高级文本编辑器都可以通过插件支持此功能),则通常只需单击两下鼠标或右键(如果已配置)就可以很容易地记录getter和setter。有时,当您基于数据库模式或WSDL生成代码时,它们会自动生成...
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner,我所谈论的那个人是要离开公司,我相信所有对吸气剂/设置者的评论都是由那个人完成的,以表明他/她做了一些努力以留下一些文档
superM 2012年

该人员发出通知后,是否输入了所有的bogo评论?我已经看到人们在各处创建空/无用的xml注释,这是阻止VS生成“对公开可见的Foo缺少xml注释”警告的明智方法。
Dan在火光中摆弄

@Dan Neely,我想这个人并不在乎,只是添加了评论说要添加评论。我们通常对注释不怎么关注,但是如果有人要离开并正在处理组件,则必须编写清晰易读的代码。
2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.