15
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C#におけるインターフェースと拡張メソッドを用いたバリエーション定義のガイドライン

Last updated at Posted at 2021-09-07

はじめに

C#のインターフェイスで様々なバリエーションを定義したい場合に拡張メソッドを用いてバリエーションの定義をすると利点が多いため、そのガイドラインを記します。

インターフェースと拡張メソッドを用いたバリエーション

  • インターフェースには、プリミティブなメソッドのみを定義し、そのバリエーションは拡張メソッドとして定義すると良い。
  • 利点
    • インターフェース実装者は最低限のインターフェースのみ実装すれば良い。
    • 単体テストでもバリエーション部分のメソッドのモック実装が不要となる。
    • さらにメソッドを増やす場合も、インターフェースが変わらないのでインターフェース実装をしている他ユーザへの影響がない。
    • フレームワーク、ライブラリ提供側は、インターフェースはプリミティブ化して、バリエーションを拡張メソッド化するアプローチを取ると良い。

公式の拡張メソッドのガイドライン

上記はマイクロソフトの拡張メソッドのガイドラインです。
どのような場合に拡張メソッドを利用すべきかが記されています。以下に例を記します。

拡張メソッドをむやみに定義することは避けてください。これは自分が所有していない型に対して特に当てはまります。
型のソース コードを所有している場合は、代わりに通常のインスタンス メソッドを使用することを検討してください。 所有していないときにメソッドを追加する場合は、細心の注意を払ってください。 拡張メソッドを自由に使用すると、それらのメソッドを使用するように設計されていなかった種類の API が乱雑になる可能性があります。

Loggerでの例

Microsof.Extensions.Loggingで同様のアプローチを取っています。

// インターフェイスでプリミティブなログのメソッドを定義する
public interface ILogger
{
    void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
    bool IsEnabled(LogLevel logLevel);
    IDisposable BeginScope<TState>(TState state);
}

// ログのバリエーションを拡張メソッドで定義する
public static class LoggerExtensions
{
    public static void LogDebug(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args)
    {
        logger.Log(LogLevel.Debug, eventId, exception, message, args);
    }
    
    ...

    public static void LogError(this ILogger logger, EventId eventId, Exception? exception, string? message, params object?[] args)
    {
        logger.Log(LogLevel.Error, eventId, exception, message, args);
    }

    ...
}

Configurationでの例

// インターフェイスでプリミティブなConfigurationのメソッドを定義する
public interface IConfiguration
{
    IConfigurationSection GetSection(string key);
    IEnumerable<IConfigurationSection> GetChildren();
    IChangeToken GetReloadToken();
}

// Configurationのバリエーションを拡張メソッドで定義する
public static class ConfigurationExtensions
{
    public static bool Exists(this IConfigurationSection section)
    {
        if (section == null)
        {
            return false;
        }
        return section.Value != null || section.GetChildren().Any();
    }
    public static string GetConnectionString(this IConfiguration configuration, string name)
    {
        return configuration?.GetSection("ConnectionStrings")?[name];
    }

    ...

}
15
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?