13,009
回編集
| 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]] | ||