明示的なインターフェイス実装を使用してC#のメンバーを非表示にすることはできますか?


14

C#でのインターフェイスおよび明示的なインターフェイスの実装の操作方法は理解していますが、頻繁に使用されない特定のメンバーを非表示にするのが悪いと考えられているのではないかと考えていました。例えば:

public interface IMyInterface
{
    int SomeValue { get; set; }
    int AnotherValue { get; set; }
    bool SomeFlag { get; set; }

    event EventHandler SomeValueChanged;
    event EventHandler AnotherValueChanged;
    event EventHandler SomeFlagChanged;
}

イベントは有用ですが、使用されることはほとんどないことを事前に知っています。したがって、IntelliSenseの乱雑さを避けるために、明示的な実装を介して実装クラスからそれらを隠すことは理にかなっていると思いました。


3
インターフェイスを介してインスタンスを参照している場合、インスタンスは非表示になりませんが、参照が具象型への場合のみ非表示になります。
アンディ14年

1
私はこれを定期的にやった人と仕事をしました。それは絶対に私を夢中にさせた。
ジョエルイーサートン14年

...集計の使用を開始します。
ミカライ14年

回答:


39

はい、それは一般的に悪い形です。

したがって、IntelliSenseの乱雑さを避けるために、明示的な実装を介して実装クラスからそれらを隠すことは理にかなっていると思いました。

IntelliSenseが散らかっている場合、クラスが大きすぎます。クラスが大きすぎる場合、多すぎることや設計が不十分である可能性があります。

症状ではなく問題を修正してください。


未使用のメンバーに関するコンパイラ警告を削除するために使用するのは適切ですか?
ガスドール14年

11
「症状ではなく、問題を修正してください。」+1
FreeAsInBeer 14年

11

目的に合わせて機能を使用します。名前空間の乱雑さを減らすことはそれらの1つではありません。

正当な理由は「隠蔽」や組織に関するものではなく、曖昧さを解決して専門化することです。「Foo」を定義する2つのインターフェイスを実装すると、Fooのセマンティクスが異なる場合があります。明示的なインターフェイスを除いて、これを解決するより良い方法ではありません。別の方法は、重複するインターフェイスの実装を許可しないか、インターフェイスがセマンティクスを関連付けられないことを宣言することです(これはとにかく概念的なことです)。どちらも貧弱な選択肢です。時には実用性が優雅さよりも優先されます。

一部の著者/専門家は、それを機能のごまかしと見なしています(メソッドは実際には型のオブジェクトモデルの一部ではありません)。しかし、すべての機能のように、どこかで誰かがそれを必要とします。

繰り返しますが、私はそれを隠蔽や組織化に使用しません。メンバーをインテリセンスから隠す方法があります。

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

「それを機能の手がかりと見なす著名な著者/専門家がいます。」誰が?提案された代替案は何ですか?少なくとも1つの問題(つまり、サブインターフェイス/実装クラスのインターフェイスメソッドの特殊化)があり、明示的な実装がない場合に解決するために他の機能をC#に追加する必要があります(ADO.NETおよびジェネリックコレクションを参照) 。
ジョミナル14年

@jhominal- Jeffrey RichterによるC#経由のCLR は、特にkludgeという単語を使用しています。C ++はそれなしで機能します。プログラマーは、明確にベースメソッドの実装を参照して明確にするか、キャストを使用する必要があります。私はすでに、代替案はあまり魅力的ではないと述べました。ただし、一般的なOOPではなく、単一の継承とインターフェイスの側面です。
コデンハイム

また、同じ「実装の単一継承+インターフェース」設計にも​​かかわらず、Javaにも明示的な実装がないことにも言及できました。ただし、Javaでは、オーバーライドされたメソッドの戻り値の型を特殊化して、明示的な実装の必要性の一部をなくすことができます。(C#では、おそらくプロパティを含めるために、おそらく別の設計上の決定が行われました)。
ジョミナル14年

@jhominal-確かに、数分後に更新します。ありがとう。
コデンハイム14年

非表示は、クラスがインターフェイスコントラクトに準拠しているが役に立たない方法でインターフェイスメソッドを実装する可能性がある状況で完全に適切な場合があります。たとえば、IDisposable.Dispose何かをするクラスの考え方は嫌いですがClose、のような別の名前が付けられているクラスは、Dispose明示的なインターフェイス実装を使用するために呼び出すことなく、コードがインスタンスを作成して放棄する可能性がある状態を明示的に放棄するクラスにとって完全に合理的であると考えますIDisposable.Dispose何もしないメソッドを定義します。
supercat

5

私は厄介なコードの兆候かもしれませんが、時には現実の世界は厄介です。.NETフレームワークの1つの例は、変更セッター[]、追加および設定を隠すReadOnlyColectionです。

IE ReadOnlyCollectionはIList <T>を実装します。 数年前に私がこれについて考えたときのSOへの私の質問も参照してください。


まあ、それは私自身も使用するインターフェースなので、最初から間違ったことをする意味はありません。
カイルバラン

2
ミューティングセッターを隠しているとは言いませんが、まったく提供していません。より良い例は、A)の消費者が直接それらを呼び出さず、B)絶対に必要な場合に実装を必要とするメソッドを使用できるようにConcurrentDictionaryIDictionary<T>明示的にのさまざまなメンバーを実装します。例えば、利用者がすべき呼び出すのではなく、不要な例外を必要とする避けるために。ConcurrentDictionaryIDictionary<T>ConcurrentDictionary<T> TryAddAdd
ブライアン14年

もちろん、Add明示的に実装することで混乱を減らすこともできます。 TryAddできることは何でもAddできます(Addへの呼び出しとして実装されているTryAddため、両方を提供することは冗長です)。ただし、TryAdd必ずしも異なる名前/署名を持っているため、IDictionaryこの冗長性なしで実装することはできません。明示的な実装により、この問題は完全に解決されます。
ブライアン14年

消費者の観点からは、メソッドは隠されています。
エスベンスコフペダーセン14年

1
IReadonlyCollection <T>について記述しますが、ReadOnlyCollection <T>にリンクします。インターフェイスの1つ目、2つ目はクラスです。IReadonlyCollection <T>は、ReadonlyCollection <T>を実装していてもIList <T>を実装しません。
ヨルゲンフォー14年

2

メンバーが文脈上無意味な場合にそうするのは良い形です。たとえばIList<T>、内部オブジェクトに委任することによって実装する読み取り専用コレクションを作成する場合、_wrapped次のようなものがあります。

public T this[int index]
{
  get
  {
     return _wrapped[index];
  }
}
T IList<T>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
public int Count
{
  get { return _wrapped.Count; }
}
bool ICollection<T>.IsReadOnly
{
  get
  {
    return true;
  }
}

ここには、4つの異なるケースがあります。

public T this[int index]インターフェースではなくクラスによって定義されているため、もちろん明示的な実装ではありませんT this[int index]が、インターフェースで定義された読み取り/書き込みに似ていますが、読み取り専用であることに注意してください。

T IList<T>.this[int index]その一部(ゲッター)が上記のプロパティと完全に一致し、他の部分は常に例外をスローするため、明示的です。インターフェイスを介してこのクラスのインスタンスにアクセスする人にとっては重要ですが、クラスの型の変数を介してこのクラスのインスタンスを使用する人にとっては意味がありません。

同様に、bool ICollection<T>.IsReadOnly常にtrueを返すため、クラスの型に対して記述されたコードにはまったく意味がありませんが、インターフェイスの型を介してそれを使用するために不可欠である可能性があるため、明示的に実装します。

逆に、public int Count明示的に実装されていないのは、独自の型を介してインスタンスを使用しているユーザーにとって潜在的に有用である可能性があるためです。

しかし、「ごくまれにしか使用されない」場合は、明示的な実装を使用しないことに強く傾倒します。

クラスの型の変数を介してメソッドを呼び出す明示的な実装を使用することをお勧めする場合、エラー(インデックス付きセッターの使用を試みる)または無意味(常に同じ値をチェックする)になるため、非表示になりますバグのあるコードや次善のコードからユーザーを保護しています。これは、ほとんど使用されないと思われるコードとは大きく異なります。そのため、EditorBrowsable属性を使用してメンバーをインテリセンスから隠すことを検討するかもしれませんが、それでも私は疲れます。人々の脳にはすでに、興味のないものを除外するための独自のソフトウェアがあります。


インターフェイスを実装する場合は、インターフェイスを実装します。それ以外の場合、これはリスコフ代替原則の明らかな違反です。
テラスティン14年

@Telastynインターフェースは実際に実装されています。
ジョンハンナ14年

しかし、その契約は満たされていません-具体的には、「これは可変のランダムアクセスコレクションを表すものです」。
Telastyn

1
@Telastynは、文書化された契約を満たします。特に、「読み取り専用のコレクションでは、コレクションの作成後に要素の追加、削除、または変更が許可されない」という観点から、msdn.microsoft.com/en-us/library/0cfatk9t%28v=vs.110%29.aspxを
ジョンハンナ

@Telastyn:コントラクトに可変性が含まれている場合、インターフェイスにIsReadOnlyプロパティが含まれるのはなぜですか?
ニキー14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.