2

次のような定義でメソッドを呼び出そうとしています (混乱を避けるために単純化されています)。

public static void Register<T>(T value) where T : BaseClass, IInterface

これらの値の両方を定義するクラス インスタンスがある限り、これは正常に機能します。「BaseClass」をメソッドに渡し、そのインスタンスを上記の宣言で使用しようとすると、問題が発生します。例えば:

public class MyClass
{
    public MyClass(BaseClass value)
    {
        Register(value);
    }
}

BaseClass と IInterface の両方を実装するクラスのインスタンスをコンストラクターに渡すことはできますが、Registerメソッドでその値を使用しようとすると、次のようなコンパイル エラーが発生します。

型 'BaseClass' は、ジェネリック型またはメソッド 'Register(T)' の型パラメーター 'T' として使用できません。「BaseClass」から「IInterface」への暗黙的な参照変換はありません。

コンストラクターで型を次のように変更すると、次のようになります。

public class MyClass
{
    public MyClass(IInterface value)
    {
        Register(value);
    }
}

次のようなエラーが表示されます。

型 'IInterface' は、ジェネリック型またはメソッド 'Register(T)' で型パラメーター 'T' として使用できません。「IInterface」から「BaseClass」への暗黙的な参照変換はありません。

これはちょっとしたキャッチ 22 のようです。 パラメータを定義して、BaseClass と IInterface の両方を実装する必要があることを示す方法はありますか?

4

2 に答える 2

7

質問を書いているときに答えを思いついたので、質問を削除する代わりに投稿することにしました。

クラスを再定義するだけです:

public class MyClass<T> where T : BaseClass, IInterface
{
    public MyClass(T value)
    {
        Register(value);
    }
}
于 2012-12-06T16:55:24.303 に答える
1

マットが提供する解決策は、渡されたオブジェクトをフィールドまたはコレクションに格納する必要がない状況に対する簡単な答えです。渡されたオブジェクトを永続化する必要がある場合、事態はさらに困難になります。SomeClass<T>whereTが複数の制約を満たし、クラス T のアイテムを格納し、それらをジェネリックとしてそのような制約を持つルーチンに渡すのは簡単ですが、クラスにメソッドSomeMethod<TParam>(TParam thing)whereがある場合TParam:IFoo,BaseBar、 type のフィールドはありませんTParam。またはthing型のフィールドに格納できますが、 を実装しない限り、または渡されたすべてのインスタンスがを実装する特定の派生物から派生することになります。IFooBaseBarBaseBarIFooBaseBarIFoo、フィールドの型が両方の制約を満たすことを指定する方法はありません(すべてのインスタンスがBaseBarを実装する特定の派生物から派生するIFoo場合、その型を単一の制約として単純に使用するか、ジェネリックをまったく気にしないでください。それをパラメーターの型として使用します)。

これらの問題を回避する方法はいくつかあります。リフレクション、私が呼び出すインターフェイス パターンISelf<T>、またはトリッキーなネストされたコールバック インターフェイスを使用します。ただし、場合によっては、二重制約パラメーターを受け取るメソッドに代わるものを提供する方がよい場合があります (メソッドが 1 つの制約型のパラメーターを受け取り、それを別の型にキャストし、コンパイル時の安全性の欠如を受け入れるようにします)。 .

于 2012-12-06T19:21:37.230 に答える