回答:
2つのインターフェースを実装し、どちらも同じメソッドと異なる実装を使用する場合は、明示的に実装する必要があります。
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
非優先メンバーを非表示にすると便利です。たとえば、両方を実装しIComparable<T>
てIComparable
いて、通常、IComparable
オーバーロードを非表示にして、さまざまなタイプのオブジェクトを比較できるという印象を人々に与えないほうがよい場合は、同様に、一部のインターフェイスはのようにCLSに準拠しIConvertible
ていません。そのため、インターフェイスを明示的に実装しない場合、CLS準拠を必要とする言語のエンドユーザーはオブジェクトを使用できません。(BCLの実装者がプリミティブのIConvertibleメンバーを隠さなかった場合、これは非常に悲惨なことになります:))
もう1つの興味深いメモは、通常、このような構成を使用すると、明示的にインターフェースを実装する構造体は、インターフェースタイプにボックス化することによってのみ呼び出すことができるということです。これを回避するには、一般的な制約を使用します。
void SomeMethod<T>(T obj) where T:IConvertible
intに渡すときにintをボックス化しません。
string
ジェネリックの前に出回っていましたが、この習慣は流行していました。.net 2が登場したとき、彼らはのパブリックインターフェイスを壊したくなかったstring
ので、安全策を講じていたのでそのままにしておきました。
インターフェースを明示的に実装するいくつかの追加の理由:
下位互換性:ICloneable
インターフェースが変更された場合でも、メソッドクラスメンバーの実装は、メソッドシグネチャを変更する必要はありません。
よりクリーンなコード:Clone
メソッドがICloneableから削除されるとコンパイラエラーが発生しますが、メソッドを暗黙的に実装すると、未使用の「孤立した」パブリックメソッドが発生する可能性があります
強い型付け:例を使用してsupercatのストーリーを説明するために、これは私の好ましいサンプルコードです。ICloneable
明示的に実装Clone()
すると、MyObject
インスタンスメンバーとして直接呼び出すときに強く型付けすることができます。
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
interface ICloneable<out T> { T Clone(); T self {get;} }
ます。ICloneable<T>
T には意図的に制約がないことに注意してください。オブジェクトは通常、そのベースが可能な場合にのみ安全に複製できますが、できないクラスのオブジェクトを安全に複製できる基本クラスから派生したい場合があります。それを可能にするために、継承可能なクラスがパブリッククローンメソッドを公開しないようにすることをお勧めします。代わりに、protected
複製メソッドを持つ継承可能なクラスと、それらから派生し、公開複製を公開するシールされたクラスがあります。
別の便利な手法は、関数のメソッドのパブリック実装に、インターフェースで指定された値よりも具体的な値を返すようにすることです。
たとえば、オブジェクトはを実装できますがICloneable
、公開されているClone
メソッドは独自の型を返します。
同様に、IAutomobileFactory
はManufacture
を返すメソッドを持っている可能性がありますAutomobile
がFordExplorerFactory
、を実装IAutomobileFactory
するはそのManufacture
メソッドがFordExplorer
(から派生するAutomobile
)を返す可能性があります。型キャストする必要なしにによって返されたオブジェクトの-use固有のプロパティをFordExplorerFactory
使用できることを知っているコードは、何らかのタイプがあることを単に知っているコードは、その戻りをとして処理するだけです。FordExplorer
FordExplorerFactory
IAutomobileFactory
Automobile
同じメンバー名とシグニチャーを持つ2つのインターフェースがあり、使用方法に応じてその動作を変更したい場合にも役立ちます。(私はこのようなコードを書くことをお勧めしません):
interface Cat
{
string Name {get;}
}
interface Dog
{
string Name{get;}
}
public class Animal : Cat, Dog
{
string Cat.Name
{
get
{
return "Cat";
}
}
string Dog.Name
{
get
{
return "Dog";
}
}
}
static void Main(string[] args)
{
Animal animal = new Animal();
Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
Dog dog = animal;
Console.WriteLine(cat.Name); //Prints Cat
Console.WriteLine(dog.Name); //Prints Dog
}
public class Animal : Cat, Dog
つまり、インターフェースを明示的に実装するためにパブリックインターフェースをよりクリーンに保つことができます。つまり、File
クラスがIDisposable
明示的に実装Close()
し、コンシューマにとってDispose(
)よりも意味のあるパブリックメソッドを提供する場合があります。
F#は明示的なインターフェイスの実装のみを提供するため、その機能にアクセスするには常に特定のインターフェイスにキャストする必要があります。これにより、インターフェイスが非常に明示的に(しゃれがなく)使用されます。
Dispose
にする必要があるのは、クリーンアップを必要としないものだけです)。より良い例は、不変コレクションのの実装のようなものですIList<T>.Add
。
内部インターフェイスがあり、クラスのメンバーをパブリックに実装したくない場合は、それらを明示的に実装します。暗黙的な実装は公開する必要があります。
明示的な実装のもう1つの理由は、保守性です。です。
クラスが "ビジー"になった場合(そうなったとしても、他のチームメンバーのコードをリファクタリングする贅沢さはありません)、明示的な実装があると、インターフェイスコントラクトを満たすためのメソッドがそこにあることが明らかになります。
したがって、コードの「読みやすさ」が向上します。
#region
適切なタイトル文字列を使用して、それが目的です。そして、メソッドに関するコメント。
別の例を以下に示します。 System.Collections.Immutable
、作成者がこの手法を使用して、コレクション型の使い慣れたAPIを保持しながら、新しい型に意味のないインターフェースの部分を削り取りました。
具体的には、ImmutableList<T>
器具IList<T>
したがってICollection<T>
(順番に許可するImmutableList<T>
レガシーコードとより容易に使用される)、まだvoid ICollection<T>.Add(T item)
ため意味をなさないImmutableList<T>
:不変のリストに要素を追加するので、既存のリストを変更してはならない、ImmutableList<T>
また、由来しIImmutableList<T>
、そのIImmutableList<T> Add(T item)
ために使用することができます不変のリスト。
したがって、の場合Add
、実装はImmutableList<T>
次のようになります。
public ImmutableList<T> Add(T item)
{
// Create a new list with the added item
}
IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);
void ICollection<T>.Add(T item) => throw new NotSupportedException();
int IList.Add(object value) => throw new NotSupportedException();
明示的に定義されたインターフェースの場合、すべてのメソッドは自動的にプライベートになり、アクセス修飾子をパブリックに与えることはできません。と仮定します:
interface Iphone{
void Money();
}
interface Ipen{
void Price();
}
class Demo : Iphone, Ipen{
void Iphone.Money(){ //it is private you can't give public
Console.WriteLine("You have no money");
}
void Ipen.Price(){ //it is private you can't give public
Console.WriteLine("You have to paid 3$");
}
}
// So you have to cast to call the method
class Program
{
static void Main(string[] args)
{
Demo d = new Demo();
Iphone i1 = (Iphone)d;
i1.Money();
((Ipen)i1).Price();
Console.ReadKey();
}
}
// You can't call methods by direct class object
これは、明示的なインターフェイスを作成する方法です 。2つのインターフェイスがあり、両方のインターフェイスに同じメソッドがあり、単一のクラスがこれらの2つのインターフェイスを継承している場合、1つのインターフェイスメソッドを呼び出すと、コンパイラーはどのメソッドを呼び出すか混乱しました。明示的インターフェースを使用してこの問題を管理します。ここに私が下に挙げた一例があります。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oops3
{
interface I5
{
void getdata();
}
interface I6
{
void getdata();
}
class MyClass:I5,I6
{
void I5.getdata()
{
Console.WriteLine("I5 getdata called");
}
void I6.getdata()
{
Console.WriteLine("I6 getdata called");
}
static void Main(string[] args)
{
MyClass obj = new MyClass();
((I5)obj).getdata();
Console.ReadLine();
}
}
}