5

<X super MySubClass>C# に Java の一般的な制約と同等のものがあるかどうか疑問に思っています。

上限を指定するには を使用できclass Foo<T> where T : MySuperClass { ... }ますが、ジェネリック パラメーターの下限を指定するにはどうすればよいでしょうか。


同様の結果を得るにはいくつかの方法がありますが、まだ完璧なものを見つけていません。

  1. 2 番目の汎用パラメーターを使用しますが、呼び出し元は実際の下限のサブクラスを指定できます。

    public class Foo<T, TLowerBound>
        where TLowerBound : MySubClass
        where TLowerBound : T
    {
        ...
    }
    
  2. これは拡張メソッドで使用されることがあるため、拡張メソッドのパラメーターUはクラスのパラメーターのスーパークラスになるように制限されますT

    public static class Extensions {
        public static void Method<T, U>(this Foo<T> self, U someU) where T : U {
            self.ValueOfTypeT = someU;
        }
    }
    
  3. インターフェイスでバリアンスを使用しますが、これを使用してジェネリック パラメーターの下限を指定できるかどうかはわかりません。

4

1 に答える 1

2

私はちょうど同じ問題に直面しました。オプション 2 (拡張メソッドを使用) は、メソッドの下限を仮想にする (したがって、オブジェクトの動的な型に基づいてディスパッチする) 必要があるまでは、非常にうまく機能します。それが必要な場合は、オプション 3 (インターフェイスの分散とよく知られているビジター パターン) を使用した実行可能なソリューションを次に示します。

同等を達成するために

public class A<T> // an argument to the generic method
{
}

public class B<S>
{
    public virtual R Fun<T>(A<T> arg) where S : T // illegal in C#/CLR
    {
        ...
    }
}

public class C<S> : B<S>
{
    public override R Fun<T>(A<T> arg)
    {
    }
}

次のことを行います。まず、操作を実行するためのインターフェースを定義します (ここではビジター パターンを使用するため、オーバーライドする型ごとに個別のメソッドが必要ですFun)。

public interface IFun<in T>
{
    R Fun<S>(B<S> self) where S : T;
    R Fun<S>(C<S> self) where S : T;
}

ジェネリック パラメータTは制約としてのみ使用されるため、インターフェイスはそれに関して反変である可能性があることに注意してください。これを使用して、操作BによってC「訪問」されます。

public class B<S>
{
    public virtual R Perform(IFun<S> fun)
    // contravariant, any IFun<T> with S : T will be accepted
    {
        return fun.Fun(this);
    }
}

public class C<S> : B<S>
{
    public override R Perform(IFun<S> fun)
    {
        return fun.Fun(this);
    }
}

実際に引数を使用して操作を実行するにはA<T>、インターフェイスを実装する構造体/クラスでラップします。

public struct TheFun<T> : IFun<T>
{
    public A<T> arg;

    R IFun<T>.Fun<S>(B<S> self)
    {
        ... body of B<S>.Fun(A<T> arg) ...
    }

    R IFun<T>.Fun<S>(C<S> self)
    {
        ... body of C<S>.Fun(A<T> arg) ...
    }
}

最後に、オプション 2 のように拡張メソッドを導入します。

public static class Extensions
{
    public static R Fun<S,T>(this B<S> self, A<T> arg) where S : T
    {
        return self.Perform(new TheFun<T> { arg = arg });
    }
}

終わり。単一のキャストや型チェックなしで機能します。主な欠点は次のとおりです。

  • それは非常に複雑で (ただし、コード サイズは一定の要因だけ意図したものよりも長くなります)、コードを読んでいる人に嫌われる可能性があります。
  • 実装は からBおよびCに移動されたため、およびTheFunの必要なメンバーはそこでアクセスできるようにする必要があります。BC
于 2014-08-20T18:53:08.433 に答える