Комментарий Наследование для С# (фактически любого языка)

Предположим, что у меня есть этот интерфейс

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

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

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

И этот класс

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

Есть ли способ, или есть инструмент, который может позволить мне автоматически помещать комментарии каждого члена в базовый класс или интерфейс?

Потому что я ненавижу переписывать те же комментарии для каждого производного подкласса!

Ответ 1

GhostDoc делает именно это. Для методов, которые не наследуются, он пытается создать описание из имени.

FlingThing() становится "Flings the Thing"

Ответ 2

Вы всегда можете использовать тег <inheritdoc />.

public class Foo : IFoo
{
    /// <inheritdoc />
    public void Foo() { ... }
    /// <inheritdoc />
    public void Bar() { ... }
    /// <inheritdoc />
    public void Snafu() { ... }
}

Ответ 3

Используйте /// <inheritdoc/>, если вы хотите наследования. Избегайте GhostDoc или чего-либо подобного.

Я согласен, это раздражает, что комментарии не наследуются. Было бы довольно просто создать надстройку, если бы у кого-то было время (хотелось бы).

Тем не менее, в нашей кодовой базе мы размещаем комментарии XML только для интерфейсов и добавляем дополнительные комментарии реализации к классу. Это работает для нас, так как наши классы являются частными/внутренними, и только интерфейс является общедоступным. Каждый раз, когда мы используем объекты через интерфейсы, у нас отображаются полные комментарии в виде интеллигентности.

GhostDoc - хорошее начало, благодаря которому процесс написания комментариев стал проще. Это особенно полезно для поддержания комментариев актуальными, когда вы добавляете/удаляете параметры, перезапускаете GhostDoc, и это обновит описание.

Ответ 4

Java имеет это, и я использую его все время. Просто выполните:

/**
 * {@inheritDoc}
 */

И инструмент Javadoc показывает это.

С# имеет аналогичный маркер:

<inheritDoc/>

Вы можете прочитать здесь:

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

Ответ 5

У Resharper есть возможность скопировать комментарии из базового класса или интерфейса.

Ответ 6

Я бы сказал, чтобы напрямую использовать

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

И

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

Вы должны поместить эти комментарии только в предыдущую строку вашего класса/метода

Это позволит получить информацию о ваших комментариях, например, из интерфейса, который вы задокументировали, например:

    /// <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);

Ответ 7

Другой способ - использовать тег документации XML <see />. Это дополнительное усилие, но оно работает "из коробки"...

Вот несколько примеров:

/// <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() { ... }
}

Обновление:

Теперь я предпочитаю использовать /// <inheritdoc/>, который теперь поддерживается ReSharper.

Ответ 8

Я закончил тем, что создал инструмент для пост-обработки файлов документации XML, чтобы добавить поддержку для замены тега <inheritdoc/> в самих файлах документации XML. Доступно на www.inheritdoc.io (доступна бесплатная версия).

Ответ 9

Ну, есть некое родное решение, которое я нашел для .NET Core 2.2

Идея состоит в том, чтобы использовать тег <include>.

Вы можете добавить <GenerateDocumentationFile>true</GenerateDocumentationFile> свой .csproj файл.

У вас может быть интерфейс:

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

И что-то от этого наследует:

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.");
    }
}

Хорошо, это немного страшно, но оно добавляет ожидаемые элементы к YourNamespace.xml.

Если вы строите конфигурацию Debug, вы можете заменить Release на Debug в атрибуте file тега include.

Чтобы найти правильный member name для ссылки, просто откройте сгенерированный файл Documentation.xml.

Я также предполагаю, что этот подход требует, чтобы проект или решение создавались как минимум дважды (первый раз для создания исходного XML файла, а второй - для копирования элементов из него в себя).

Яркой стороной является то, что Visual Studio проверяет скопированные элементы, поэтому намного проще синхронизировать документацию и код с интерфейсом/базовым классом и т.д. (Например, имена аргументов, имена параметров типа и т.д.).

В моем проекте я получил <inheritdoc/> (для DocFX) и <include/> (для публикации пакетов NuGet и для проверки в 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();