スポンサーリンク

C# 8のinterfaceのデフォルト実装

Default implementations in interfaces With last week’s posts Announcing .NET Core 3.0 Preview 5 and Visual Studio 2019 version 16.1 Preview 3, the last major feature of C# 8.0 is now available in preview. A big impediment to software evolution has been the fact that you couldn’t add new members to a public interface.

情報源: Default implementations in interfaces | .NET Blog

C# 8から導入されるinterfaceのデフォルト実装についての記事。

以下はテキトー訳

インタフェースに、そのメンバーの実装を記述できるようになります。実装したクラスもしくは構造体がそのメンバの実装を提供しなくてもエラーにはならず、interfaceで記述されたそのメンバのコードが実行されます。公開済みライブラリのインタフェースにメンバを追加する必要が出てきたときに、既存のそのインタフェースを実装した型に影響を与えることなく、追加することができます。

次のようなインタフェースがあるとします。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">interface</span> <span class="hljs-title">ILogger</span>
{
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Log</span>(<span class="hljs-params">LogLevel level, <span class="hljs-keyword">string</span> message</span>)</span>;
}
<span class="hljs-keyword">interface</span> <span class="hljs-title">ILogger</span> { <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Log</span>(<span class="hljs-params">LogLevel level, <span class="hljs-keyword">string</span> message</span>)</span>; }
interface ILogger
{
    void Log(LogLevel level, string message);
}

既存のクラスは、恐らく所有者が異なる別のコードベースにあり、次のようにILoggerを実装しています。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">class</span> <span class="hljs-symbol">ConsoleLogger</span> : <span class="hljs-symbol">ILogger</span>
{
<span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> Log(LogLevel level, <span class="hljs-built_in">string</span> message) { ... }
}
<span class="hljs-keyword">class</span> <span class="hljs-symbol">ConsoleLogger</span> : <span class="hljs-symbol">ILogger</span> { <span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> Log(LogLevel level, <span class="hljs-built_in">string</span> message) { ... } }
class ConsoleLogger : ILogger
{
    public void Log(LogLevel level, string message) { ... }
}

次のようにILoggerインタフェースにLogメソッドのオーバーロードを追加したいと思います。デフォルトの実装、つまりメソッド本体を提供することで、既存の実装を壊すことなく実行できます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
interface ILogger
{
void <span class="hljs-constructor">Log(LogLevel <span class="hljs-params">level</span>, <span class="hljs-params">string</span> <span class="hljs-params">message</span>)</span>;
void <span class="hljs-constructor">Log(Exception <span class="hljs-params">ex</span>)</span> => <span class="hljs-constructor">Log(LogLevel.Error, <span class="hljs-params">ex</span>.ToString()</span>);
}
interface ILogger { void <span class="hljs-constructor">Log(LogLevel <span class="hljs-params">level</span>, <span class="hljs-params">string</span> <span class="hljs-params">message</span>)</span>; void <span class="hljs-constructor">Log(Exception <span class="hljs-params">ex</span>)</span> => <span class="hljs-constructor">Log(LogLevel.Error, <span class="hljs-params">ex</span>.ToString()</span>); }
interface ILogger
{
    void Log(LogLevel level, string message);
    void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());
}

これは、ConsoleLoggerが必要とするインタフェース規約を満たしています。そのインスタンスが、インタフェースに変換されて、新しいLogのオーバーロードが呼ばれた場合には、インタフェースで記述したデフォルト実装が呼び出されます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public static void <span class="hljs-constructor">LogException(ConsoleLogger <span class="hljs-params">logger</span>, Exception <span class="hljs-params">ex</span>)</span>
{
ILogger ilogger = logger; <span class="hljs-comment">// Converting to interface</span>
ilogger.<span class="hljs-constructor">Log(<span class="hljs-params">ex</span>)</span>; <span class="hljs-comment">// Calling new Log overload</span>
}
public static void <span class="hljs-constructor">LogException(ConsoleLogger <span class="hljs-params">logger</span>, Exception <span class="hljs-params">ex</span>)</span> { ILogger ilogger = logger; <span class="hljs-comment">// Converting to interface</span> ilogger.<span class="hljs-constructor">Log(<span class="hljs-params">ex</span>)</span>; <span class="hljs-comment">// Calling new Log overload</span> }
public static void LogException(ConsoleLogger logger, Exception ex)
{
    ILogger ilogger = logger; // Converting to interface
    ilogger.Log(ex);          // Calling new Log overload
}

もちろん、新しいメンバーについて知っている実装クラスは、デフォルト実装が存在するインタフェースメンバーに対しても、独自の方法でそれを実装する事ができます。

以下にチュートリアルが用意されています。

Safely update interfaces using default interface members in C# | Microsoft Docs

タイトルとURLをコピーしました