4

「インターフェイス IFoo および IBar を実装するオブジェクトを期待するメソッド」を表現したいとしましょう。何かのようなもの:

void Method(IFoo+IBar param);

C# を使用してこれをどのように言えますか?

(構文構造はありますか? または、おそらく適切な慣用句はありますか?)


試み

インターフェイスを導入します。

interface IFooBar : IFoo, IBar {}

void Method(IFooBar param);

これは悪いことであり、考えたことを後悔しています:-) 一見問題ないように見えますが、悲しいことに、ここにあってはならない依存関係が静かに導入されます。

が存在する唯一の理由は、のインターフェースのIFooBar一部になることです。Methodしたがって、Methodのパラメーターとして使用されるオブジェクトのクラスは、このメソッド (およびそのインターフェイス) が存在することを認識している必要があります。それ以外の場合はそうではありません。IFoo と IBar を実装するすべてのクラスは、IFooBar も実装するように変更する必要があります (おそらく、新しいアセンブリを知る必要があります)。これは非常に非現実的であり、変更できない場合は不可能ですらあります。

不要な依存 = ダメ。

回避策

静的型付けをあきらめる:

void Method(object param)
{
    if (!param is IFoo)
        throw new ArgumentException("IFoo not supported", "param");
    if (!param is IBar)
        throw new ArgumentException("IBar not supported", "param");

    // ...
}

(または: 署名で 1 つのタイプを定義し、他のタイプを動的にチェックします。1 つが最も重要な場合は望ましいですが、さらに混乱を招きます)

正しく動作しますが、実行時に動作します (コンパイル時のチェックはありません)。必死にドキュメントを必要とします (そしてそれを読むために誰もが)。

また、関数パラメーターでのみ実用的です。フィールドで使用しようとすると、キャストでコードが大幅に肥大化します。


この状況の実際のケースは?たとえばIList<Foo>+ INotifyCollectionChanged.

4

4 に答える 4

9
public void someMethod<T>(T param) where T : IFoo, IBar
{...}
于 2012-04-27T13:54:30.180 に答える
2

私の友人がエリック・リッパートに非常によく似た質問をしたことがあり、これが彼の答えでした:

Q: C# で複数のインターフェイスによる変数の宣言が許可されない理由はありますか?

A: {IFoo, IBar} と注釈を付けたとしましょう。

問題は、変数の宣言よりも大きいです。基本的に、変数の制約を「変数は特定の型でなければならない」から「変数はすべて特定の型でなければならない」に変更したいと言っています。しかし、なぜ変数で止まるのでしょうか? それが変数の型制約である場合、それはピリオド型である必要があります。あなたは言うことができるはずです:

List< { IFoo, IBar } > myList; Or
 public static { IFoo, IBar } MyMethod( { IFoo, IBar }[ ] foobars  )
 {  return foobars[0];  }

等々。機能を途中で実行しても意味がありません。

これは、型システムの主要な拡張となります。C# や VB だけでなく、すべてのマネージ言語でサポートしたいと考えています。これは、バージョン 1 のフレームワークに組み込みたい機能の一種です。4 つのバージョンがあり、何十億行もの顧客コードが動作し続けなければならない場合、このような大きな変更を行うのは非常に費用がかかり、困難です。

私はしばしばその機能を自分で欲しがっていました。これが素晴らしい機能であることには同意しますが、今追加するにはコストが高すぎると思います。次に新しい型システムを設計するときは、その機能を最初から含めてください!

于 2012-04-27T13:55:17.683 に答える
2

以下のようにメソッドを定義することでそれを達成できます

void Method<T>(T param) 
    where T: IFoo, IBar
{
}

お役に立てれば。

于 2012-04-27T13:57:13.610 に答える
1

メソッドが戻った後に永続化されないものには、制約付きのジェネリックパラメーターを使用できます。残念ながら、それらには大きな制限があります。次のような方法:

void foo(T param)where T:IFoo、IBar {...}

コンパイル時に、およびを実装する特定のタイプTであることがわかっている場合は、簡単に呼び出すことができます。残念ながら、両方のインターフェイスを実装する共通のベースタイプをすべて共有せずに、上記のようなルーチンに渡すことができるオブジェクトのコレクションを構築することは困難です。「との両方を実装し、同じことを期待するルーチンに渡すことができるもの」のコレクションを保持しようとする場合、それを行う方法はありますが、簡単ではありません。IFooIBarIFooIBar

与えられたオブジェクトを渡す可能性のあるすべてのルーチンを事前に特定できれば、与えられたオブジェクトごとに、コレクションに閉じたジェネリックデリゲートを作成させることができます。これは機能し、実行可能な状況ではかなり効率的かもしれません。ただし、コレクションクラスとコレクション内のオブジェクトの使用との間に非常に強い依存関係が導入されます。

もう少し用途の広いアプローチは、コレクションに、内部オブジェクトに対して任意の制約付きオープンジェネリックアクションを実行するメソッドをサポートするラッパーオブジェクトを保持させることです。残念ながら、デリゲートはこれにはうまく機能しません。.netは、最初にReflectionを使用して閉じた形式に変換せずに、開いているジェネリックデリゲートを呼び出すメカニズムを提供しないためです。代わりに、次のようなインターフェイスを定義する必要があります。

インターフェイスIActUpon<T、U> {
  void DoSomething <MT>(ref MT it)ここで、MT:T、U;
}
インターフェイスIActUpon<T、U、PT1> {
  void DoSomething <MT>(ref MT it、ref PT1 p1)ここで、MT:T、U;
}
インターフェイスIActUpon<T、U、PT1、PT2> {
  void DoSomething <MT>(ref MT it、ref PT1 p1、ref PT2 p2)ここで、MT:T、U;
}

オブジェクトがメソッドファミリーのメソッドをサポートしている場合:

void DoSomethingWithMe(IActUpon <T、U> proc);
void DoSomethingWithMe(IActUpon <T、U、PT1> proc、ref PT1 p1);
void DoSomethingWithMe(IActUpon <T、U、PT1、PT2> proc、ref PT1 p1、ref PT2 p2);

の適切なバリエーションを実装する単純なクラスを作成することにより、タイプTおよびUに制約されたパラメーターを必要とする任意のルーチンをオブジェクトに呼び出させることができますIActUpon<T,U,...>

残念ながら、そのアプローチはほぼ一般的ですが(最大の制限は、特定の数のジェネリックパラメーターを明示的にコーディングする必要があることです)、せいぜい厄介です。一部のコンパイラサポートでは、それほど悪くはないかもしれませんが、そうでなければ、一般的な使用にはおそらく厄介です。

于 2012-04-27T15:39:01.347 に答える