C# 指明;“任何子类”;在C类型约束中,而不是;一个特殊的子类";

C# 指明;“任何子类”;在C类型约束中,而不是;一个特殊的子类";,c#,polyvariadic,C#,Polyvariadic,如果我想写一个方法,它接受一个变量“TDerived”,其中TDerived是类“Base”的任何子类,有什么方法可以做到这一点吗 以下代码仅适用于单个特定的指定子类: void doStuff<TDerived>(params TDerived[] args) where TDerived : Base { //stuff } 那我就不行了 Sub0 s0 = new Sub0(); Sub1 s1 = new Sub1(); doStuff(s0, s1); 因为我得

如果我想写一个方法,它接受一个变量“TDerived”,其中TDerived是类“Base”的任何子类,有什么方法可以做到这一点吗

以下代码仅适用于单个特定的指定子类:

void doStuff<TDerived>(params TDerived[] args) where TDerived : Base
{
    //stuff
}
那我就不行了

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
        }
    }
}
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 { }  
void doStuff(参数SomeReadOnlyCollection[]args),其中TDerived:Base
{
foreach(参数中的变量列表)
{
foreach(列表中的派生对象)
{
//东西
}
}
}

你当然可以改变

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

如果Super被证明是有效的


我可能误解了你的意思,但是让方法接受基类的任何子类的唯一方法是声明方法引用基类。

在你的例子中,你实际上是在告诉编译器,
doStuff
的所有参数在编译时必须是同一类型的,并且该类型必须从
Base
继承。如果希望允许参数具有不同的类型,则不要使用泛型:

void doStuff(params Base[] args)
{}
编辑

这同样适用于您的新示例-您可以使用
IEnumerable
,而不是特定的
SomeReadOnlyCollection
,如下所示:

void doStuff(参数IEnumerable[]args)
{
foreach(参数中的变量列表)
{
foreach(列表中的变量)
{
}
}
}

t派生的
需要能够解析为单个类型。在您的示例中,它可以解析为的唯一类型是
Super
,但编译器不会做出这种飞跃。您可以为编译器实现这一飞跃

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

IEnumerable
是自.NET 2.0以来由BCL集合实现的,包括
T[]
List
ReadOnlyCollection
HashSet
,等等。

您可以使用的另一种方法是简单地显式指定泛型参数。例如:

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

doStuff<Super>(s0, s1);

调用函数时只需隐式指定基类型:doStuff(s0,s1);增加了一个更复杂的例子
doStuff(new Super[] { s0, s1 });
doStuff<Super>(s0, s1);
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 { }  
var s0 = new Sub0();
var s1 = new Sub1();

doStuff<Super>(s0, s1);
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);