「C Sharpの基礎 - インターフェイス」の版間の差分

ナビゲーションに移動 検索に移動
372行目: 372行目:
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
== 明示的実装 ==
インターフェイスは、1つのクラスで複数のインターフェイスを実装することができる。<br>
この時、複数のインターフェイスに同名・同引数のメソッドが存在する場合、衝突が起きる。<br>
そのため、衝突を回避するためには、インターフェイスの明示的実装を行う。<br>
<br>
メンバを定義する時、メンバ名の前に<code>インターフェイス名 + .</code>を記述する。<br>
例えば、メソッドの場合は、以下のように記述する。この時、アクセス修飾子(<code>public</code>や<code>private</code>等は指定できない)<br>
戻り値の型 インターフェイス名.メソッド名(引数一覧)
{
    メソッド本体(具体的な処理)
}
<br>
明示的実装は、メンバ単位で切り替えることができる。<br>
以下の例では、Addメソッドのみが明示的実装であり、SumメソッドやItemsメソッドは通常の(暗黙的な)実装である。<br>
<syntaxhighlight lang="c#">
using System.Collections.Generic;
interface IAccumulator
{
    void Add(int value);
    int Sum { get; }
}
interface IGroup<T>
{
    void Add(T item);
    IEnumerable<T> Items { get; }
}
/// <summary>
/// <see cref="IAccumulator.Add(int)"/>と、<see cref="IGroup{int}.Add(int)"/>が完全に被るので、
/// 別の実装を与えたければ明示的実装が必要。
/// </summary>
class ExplicitImplementation : IAccumulator, IGroup<int>
{
    void IAccumulator.Add(int value) => Sum += value;
    void IGroup<int>.Add(int item) => _items.Add(item);
    public IEnumerable<int> Items => _items;
    private List<int> _items = new List<int>();
    public int Sum { get; private set; }
}
</syntaxhighlight>
<br>
また,明示的実装を記述したメンバは、そのクラスの変数から直接使用できないため、<br>
インターフェイスのキャストしてから呼び出すことになる。<br>
<br>
インターフェイスの明示的実装を使用すると、以下のような状態になる。<br>
* 同名のメンバを持つインターフェイスを複数実装できる。
* 明示的実装したメンバは、インターフェイス型にキャストしてから使用する。
<syntaxhighlight lang="c#">
using System;
class Sample
{
    static void Accumulate(IAccumulator x, int value) => x.Add(value);
    static void AddItem<T>(IGroup<T> g, T item) => g.Add(item);
    public static void Main()
    {
      // 明示的実装を記述して2つのAddを別実装しているため、個別集計される
      var b = new ExplicitImplementation();
      for (int i = 0; i < 5; i++)
      {
          Accumulate(b, i);
          AddItem(b, i);
          // 明示的実装の場合、インターフェイスにキャストして、Add(i)を呼ぶ
          // 例えば、以下の記述はコンパイルエラーとなる
          // b.Add(i);
      }
      Console.WriteLine($"sum = {b.Sum}, items = {string.Join(", ", b.Items)}");
    }
}
</syntaxhighlight>
<br>
以下に、インターフェイスの明示的実装の用途を示す。<br>
<br>
==== メソッドの隠蔽 ====
明示的実装の性質を使用して、使用されない、または、使用されたくないメンバを隠蔽することができる。<br>
<br>
.NET Frameworkの標準ライブラリには、隠蔽したいメソッドの例として、以下のようなものが存在する。
* 非ジェネリックのIEnumerableインターフェイス(System.Collections名前空間)<br>ジェネリックのIEnumerable<T>(System.Collections.Generic名前空間)は、この非ジェネリックから派生している。
* ICollection<T>インターフェイス(System.Collections.Generic名前空間)のIsReadOnlyメソッド
<br>
以下の例では、IEnumerableインターフェイスを隠蔽している。<br>
<syntaxhighlight lang="c#">
using System.Collections;
using System.Collections.Generic;
class LinkedList<T> : IEnumerable<T>
{
    public LinkedList(T value) : this(value, null)
    { }
    private LinkedList(T value, LinkedList<T> next)
    { Value = value; Next = next; }
    public T Value { get; }
    public LinkedList<T> Next { get; }
    public LinkedList<T> Add(T value) => new LinkedList<T>(value, this);
    public IEnumerator<T> GetEnumerator()
    {
      if(Next != null)
      {
          foreach (var x in Next)
          {
            yield return x;
          }
        }
        yield return Value;
    }
    // 明示的実装
    // IEnumerableを介さない限り見えない
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
</syntaxhighlight>
<br>
※注意<br>
インターフェイスの増加はコストが掛かるため、.NET Frameworkの初期では、インターフェイスを減少させる方向で設計を進めていた。<br>
しかし現在では、"読み取り専用なコレクション"と"書き換え可能なコレクション"を別インターフェイスに分けるべきとの設計方針である。<br>
<br>
==== メンバのアクセスの制限 ====
* internal setを隠す
* internal interfaceとの組み合わせ
<br>
==== オブジェクトとジェネリック ====
特定のインターフェイスを実装している時だけ特別な動作をする、という処理を記述する場合がある。<br>
* as判定用に、interface IX { object X { get; } }
* 手動で使用するためにジェネリックも記述して、interface IX<T> : IX { new T X { get; } }
<br><br>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:C_Sharp]]
[[カテゴリ:C_Sharp]]

案内メニュー