Herança de comentários para C # (na verdade, qualquer linguagem)

93

Suponha que eu tenha esta interface

public interface IFoo
{
    ///<summary>
    /// Foo method
    ///</summary>
    void Foo();

    ///<summary>
    /// Bar method
    ///</summary>
    void Bar();

    ///<summary>
    /// Situation normal
    ///</summary>
    void Snafu();
}

E esta aula

public class Foo : IFoo
{
    public void Foo() { ... }
    public void Bar() { ... }
    public void Snafu() { ... }
}

Existe uma maneira ou existe uma ferramenta que pode me deixar colocar automaticamente os comentários de cada membro em uma classe base ou interface?

Porque eu odeio reescrever os mesmos comentários para cada subclasse derivada!

jumpinjackie
fonte
13
Não só odeio, mas também é difícil mantê-los sincronizados.
Olivier Jacot-Descombes

Respostas:

17

GhostDoc faz exatamente isso. Para métodos que não são herdados, ele tenta criar uma descrição a partir do nome.

FlingThing() torna-se "Flings the Thing"

James Curran
fonte
2
GhostDoc é incrível, uma daquelas coisas que eu não sabia que precisava, mas agora não posso
passar
178
Documentos gerados automaticamente parecem uma ideia muito ruim para mim. Eles não adicionam nenhuma informação útil, mas apenas explodem o código desnecessariamente. Se uma ferramenta pode entender o que um método faz a partir de seu nome, então uma pessoa também pode entender e nenhum documento é necessário.
Lensflare de
8
@Lensflare Isso é tão verdade. Certa vez, tive que usar um framework que tinha apenas esses comentários gerados, que NÃO adicionavam informações ao método / classe. Em vez de "Este método faz isso e aquilo", os comentários eram como "Este é o método XY da classe Z". xD Além disso, você não conseguia navegar pelo código, então ele caiu para tentativa e erro. Nunca mais! :-)
itmuckel
15
@Lensflare Embora eu concorde 100% com você no que diz respeito a confiar nos AGDs como estão , devo salientar que os AGDs não devem ser usados ​​como botões mágicos "fazer tudo" como esse. Em vez disso, eles devem ser usados ​​como geradores de modelo para reduzir a quantidade de documentação clichê e repetitiva que você mesmo precisa escrever, para que possa se concentrar nas coisas importantes. --- Por exemplo, ele pode gerar os <summary>, <param>, <returns>, <throws>, etc...seções para você. Muitas vezes com resultados bons o suficiente; outras vezes, precisando de correções ou expansão, mas ainda reduzindo o esforço geral.
XenoRo
4
pessoas, a documentação não é para desenvolvedores, é para arquitetos, então tudo está coberto: "Ei, podemos ler a documentação do código do seu projeto? Claro, aqui está."
Trident D'Gao
151

Você sempre pode usar <inheritdoc />tag.

public class Foo : IFoo
{
    /// <inheritdoc />
    public void Foo() { ... }
    /// <inheritdoc />
    public void Bar() { ... }
    /// <inheritdoc />
    public void Snafu() { ... }
}
Vadim
fonte
7
Eu nem sabia que <inheritdoc /> existia ... Mas pelo que posso ver, o comentário para este método não aparece com o intellisense.
gerleim,
12
@gerleim Veja a resposta de Jeff Heaton um ano antes e o comentário abaixo dela. Sandcastle tem <inheritdoc />, não C #.
rbwhitaker
4
Eu vejo comentários da interface no intellisense com inheritdoc, e também se não há nenhum código-doc na classe derivada. Mas isso pode ser porque eu tenho resharper.
Tim Abell
9
Resharper 2017.2 melhorou o suporte para inheritdoc jetbrains.com/resharper/whatsnew
Dav Evans
3
Visual Studio Enterprise 2017 (versão 15.9.3) não mostra comentários herdados para mim.
herzbube
26

Use /// <inheritdoc/>se quiser herança. Evite GhostDoc ou algo parecido.

Concordo que é irritante que os comentários não sejam herdados. Seria um suplemento bastante simples de criar se alguém tivesse tempo (gostaria de ter).

Dito isso, em nossa base de código, colocamos comentários XML apenas nas interfaces e adicionamos comentários de implementação extras à classe. Isso funciona para nós, pois nossas classes são privadas / internas e apenas a interface é pública. Sempre que usamos os objetos por meio das interfaces, temos todos os comentários exibidos na inteligência.

GhostDoc é um bom começo e tornou o processo mais fácil de escrever comentários. É especialmente útil manter os comentários atualizados ao adicionar / remover parâmetros, executar novamente o GhostDoc e ele atualizará a descrição.

Dennis
fonte
Estou confuso - você disse para evitar o GhostDoc, mas no final você aparentemente endossou o GhostDoc ajudando a tornar as coisas mais fáceis. Você pode esclarecer o que você quer dizer?
Mike Marynowski
Obrigado @MikeMarynowski. Este é um velho conselho. Acho que queria dizer na época que GhostDoc, como qualquer outro gerador, adicionará comentários, mas com detalhes quase inúteis, por exemplo <param name="origin">The origin.</param>. Veja ghostdoc diz as coisas mais malditas para mais exemplos. O Visual Studio agora tem linting e geradores muito melhores para xmldocs para que você saiba quando os parâmetros + documentos não se alinham, então o GhostDoc (ou outras ferramentas) não são mais necessários.
Dennis
15

Java tem isso e eu uso o tempo todo. Apenas faça:

/**
 * {@inheritDoc}
 */

E a ferramenta Javadoc descobre isso.

C # tem marcador semelhante:

<inheritDoc/>

Você pode ler mais aqui:

http://www.ewoodruff.us/shfbdocs/html/79897974-ffc9-4b84-91a5-e50c66a0221d.htm

JeffHeaton
fonte
37
C # não tem o <inheritdoc/>marcador: o Sandcastle tem. shfb.codeplex.com
Eric Dand
8
Há uma solicitação de recurso de voz do usuário para adicionar <inheritdoc /> ao C #. Vote
deadlydog
1
Nem C # nem Java (nem qualquer outra linguagem de programação) possui qualquer um dos elementos "doc XML". Estes são comentários . Os compiladores não sabem nada sobre eles. Eles são estritamente usados ​​por geradores de documentação de terceiros, seja javadoc ou castelo de areia ou qualquer outro.
James Curran
4
Quando Java ou C # é declarado, GERALMENTE significa a comunidade de ferramentas associadas. Nem Java nem C # têm muita habilidade no sentido literal. Seria um argumento acadêmico afirmar que Java ou C # não têm a capacidade de se conectar a um banco de dados, porque a biblioteca de tempo de execução faz isso.
JeffHeaton
2
O Visual Studio versão 16.4.0 e mais recente fornecem intellisense para <inheritDoc />! docs.microsoft.com/en-us/visualstudio/releases/2019/…
ashbygeek
10

Eu diria para usar diretamente o

/// <inheritdoc cref="YourClass.YourMethod"/>  --> For methods inheritance

E

/// <inheritdoc cref="YourClass"/>  --> For directly class inheritance

Você deve colocar esses comentários apenas na linha anterior de sua classe / método

Isso obterá as informações de seus comentários, por exemplo, de uma interface que você documentou como:

    /// <summary>
    /// This method is awesome!
    /// </summary>
    /// <param name="awesomeParam">The awesome parameter of the month!.</param>
    /// <returns>A <see cref="AwesomeObject"/> that is also awesome...</returns>
    AwesomeObject CreateAwesome(WhateverObject awesomeParam);
Gatsby
fonte
Obrigado pelo conselho! Esta abordagem é mais explícita e resolve o problema da descrição da classe de herança da classe de objeto (mesmo ao implementar interface).
Denis Babarykin
8

Resharper tem a opção de copiar os comentários da classe base ou interface.

Svick
fonte
1
Oh? Quão? Eu uso o ReSharper e nunca vi essa opção ao implementar ou herdar uma interface ... Onde está e como você usa essa opção?
Jazimov,
2
@Jazimov Quando você Alt + Enter o método de substituição, há uma opção para "Copiar documentação da base".
svick
8

Outra maneira é usar a <see />tag de documentação XML. Este é um esforço extra, mas funciona fora da caixa ...

aqui estão alguns exemplos:

/// <summary>
/// Implementation of <see cref="IFoo"/>.
/// </summary>
public class Foo : IFoo
{
    /// <summary>
    /// See <see cref="IFoo"/>.
    /// </summary>
    public void Foo() { ... }

    /// <summary>
    /// See <see cref="IFoo.Bar"/>
    /// </summary>
    public void Bar() { ... }

    /// <summary>
    /// This implementation of <see cref="IFoo.Snafu"/> uses the a caching algorithm for performance optimization.
    /// </summary>
    public void Snafu() { ... }
}

Atualizar:

Agora prefiro usar o /// <inheritdoc/>que agora é compatível com ReSharper.

Mathias Rotter
fonte
1

Acabei criando uma ferramenta para pós-processar os arquivos de documentação XML para adicionar suporte para a substituição da <inheritdoc/>tag nos próprios arquivos de documentação XML. Disponível em www.inheritdoc.io (versão gratuita disponível).

K Johnson
fonte
0

Bem, existe um tipo de solução nativa que encontrei para o .NET Core 2.2

A ideia é usar <include>tag.

Você pode adicionar <GenerateDocumentationFile>true</GenerateDocumentationFile>seu .csprojarquivo.

Você pode ter uma interface:

namespace YourNamespace
{
    /// <summary>
    /// Represents interface for a type.
    /// </summary>
    public interface IType
    {
        /// <summary>
        /// Executes an action in read access mode.
        /// </summary>
        void ExecuteAction();
    }
}

E algo que herda disso:

using System;

namespace YourNamespace
{
    /// <summary>
    /// A type inherited from <see cref="IType"/> interface.
    /// </summary>
    public class InheritedType : IType
    {
        /// <include file='bin\Release\netstandard2.0\YourNamespace.xml' path='doc/members/member[@name="M:YourNamespace.IType.ExecuteAction()"]/*'/>
        public void ExecuteAction() => Console.WriteLine("Action is executed.");
    }
}

Ok, é um pouco assustador, mas adiciona os elementos esperados ao YourNamespace.xml.

Se você construir Debuga configuração, você pode trocar Releasepara Debugno fileatributo da includetag.

Para encontrar uma referência correta member, namebasta abrir o Documentation.xmlarquivo gerado .

Também presumo que essa abordagem requer que um projeto ou solução seja compilado pelo menos duas vezes (a primeira vez para criar um arquivo XML inicial e a segunda vez para copiar elementos dele para si mesmo).

O lado bom é que o Visual Studio valida os elementos copiados, então é muito mais fácil manter a documentação e o código em sincronia com a interface / classe base, etc (por exemplo, nomes de argumentos, nomes de parâmetros de tipo, etc).

No meu projeto, terminei com <inheritdoc/>(para DocFX) e <include/>(para publicação de pacotes NuGet e para validação no Visual Studio):

        /// <inheritdoc />
        /// <include file='bin\Release\netstandard2.0\Platform.Threading.xml' path='doc/members/member[@name="M:Platform.Threading.Synchronization.ISynchronization.ExecuteReadOperation(System.Action)"]/*'/>
        public void ExecuteReadOperation(Action action) => action();
Konard
fonte