5

TDerived がクラス "Base" の任意のサブクラスである "TDerived" の可変数を取るメソッドを書きたい場合、これを行う方法はありますか?

次のコードは、1 つの特定の指定されたサブクラスでのみ機能します。

void doStuff<TDerived>(params TDerived[] args) where TDerived : Base
{
    //stuff
}

つまり、私が持っている場合

class Super { }
class Sub0 : Super { }
class Sub1 : Super { }

それなら私にはできない

Sub0 s0 = new Sub0();
Sub1 s1 = new Sub1();
doStuff(s0, s1);

「最適なオーバーロードされた一致...いくつかの無効な引数があります」と表示されるためです。

コンパイラが型制約と可変個引数関数をどのように処理するかに関係なく、これは (私が知る限り) 完全に型安全であるように見えます。キャストできることはわかっていますが、これがタイプセーフである場合、許可しないのはなぜですか?

編集:

おそらくより説得力のある例:

void doStuff<TDerived>(params SomeReadOnlyCollection<TDerived>[] args) where TDerived : Base
{
    foreach(var list in args)
    {
        foreach(TDerived thing in list)
        {
            //stuff
        }
    }
}
4

4 に答える 4

6

あなたの例では、実際にはすべての引数がdoStuffコンパイル時に同じ型でなければならず、この型は から継承されなければならないことをコンパイラに伝えていBaseます。引数が異なる型であることを許可したい場合は、ジェネリックを使用しないでください。

void doStuff(params Base[] args)
{}

編集

同じことが新しい例にも当てはまります-共変であるため、SomeReadOnlyCollection使用できる特定の代わりに:IEnumerable

void doStuff(params IEnumerable<Base>[] args)
{
    foreach (var list in args)
    {
        foreach (var thing in list)
        {
        }
    }
}
于 2011-10-20T02:30:59.037 に答える
6

TDerived単一のタイプに解決できる必要があります。あなたの例では、解決できるSuper唯一の型は になりますが、コンパイラはその飛躍を遂げません。コンパイラを飛躍せることができます。

doStuff(new Super[] { s0, s1 });
doStuff<Super>(s0, s1);

update に関してIEnumerable<ISuper>は、(ジェネリック メソッドの代わりに)を受け入れるメソッドを定義することを検討してくださいIEnumerable<T>。また、本質的に読み取り専用および転送専用であり、ループIEnumerable<T>がある場合に最適です。foreach完全な作業例:

class Program
{
    static void Main()
    {
        var sub0s = new Sub0[] { new Sub0() };
        var sub1s = new List<Sub1> { new Sub1() };
        doStuff(sub0s, sub1s);
    }

    static void doStuff(params IEnumerable<ISuper>[] args)
    {
        foreach (var sequence in args)
        {
            foreach (var obj in sequence)
            {
                Console.WriteLine(obj.GetType());
                // you have the ability to invoke any method or access 
                // any property defined on ISuper
            }
        }
    } 
}

interface ISuper { }
class Super : ISuper { }
class Sub0 : Super { }
class Sub1 : Super { }  

IEnumerable<T>T[]List<T>ReadOnlyCollection<T>HashSet<T>などを含む .NET 2.0 以降の BCL コレクションによって実装されます。

于 2011-10-20T02:32:05.027 に答える
0

使用できるもう 1 つの代替方法は、単純にジェネリック パラメーターを明示的に指定することです。例えば:

var s0 = new Sub0();
var s1 = new Sub1();

doStuff<Super>(s0, s1);

共変SomeReadOnlyCollectionである限り、の場合に同じ原則を適用できるはずです。たとえば、次のようなコレクションです。IEnumerable

static void doStuff2<TDerived>(params IEnumerable<TDerived>[] args) where TDerived : Super {
    // ...
}

// ...

var l0 = new List<Sub0>();
var l1 = new List<Sub1>();

doStuff2<Super>(l0, l1);
于 2011-10-20T03:01:29.917 に答える
0

まあ、あなたは間違いなく変えることができます

Sub0 s0 = new Sub0();
Sub1 s1 = new Sub1();

Super s0 = new Sub0();
Super s1 = new Sub1();

Super が TDerived の場合は機能します。

私はあなたを誤解しているかもしれませんが、メソッドが基本クラスのサブクラスを取るようにする唯一の方法は、基本型への参照を取るようにメソッドを宣言することです。

于 2011-10-20T02:29:06.890 に答える