在C#类型约束中指定"任何子类"而不是"一个特定子类"

Luc*_*ina 5 c# polyvariadic

如果我想编写一个方法,它采用可变数量的"TDerived",其中TDerived是类"Base"的任何子类,有没有办法做到这一点?

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

void doStuff<TDerived>(params TDerived[] args) where TDerived : Base
{
    //stuff
}
Run Code Online (Sandbox Code Playgroud)

即如果我有

class Super { }
class Sub0 : Super { }
class Sub1 : Super { }
Run Code Online (Sandbox Code Playgroud)

那我就做不到了

Sub0 s0 = new Sub0();
Sub1 s1 = new Sub1();
doStuff(s0, s1);
Run Code Online (Sandbox Code Playgroud)

因为我得到"最好的重载匹配......有一些无效的论点".

无论编译器如何处理类型约束和可变参数函数,这似乎(据我所知)完全类型安全.我知道我可以施放,但如果这是类型安全的,为什么不允许它?

编辑:

也许是一个更有说服力的例子:

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

Boj*_*nik 6

在您的示例中,您实际上是告诉编译器在编译时所有参数doStuff必须是相同的类型,并且必须继承此类型Base.如果要允许参数具有不同的类型,那么就不要使用泛型:

void doStuff(params Base[] args)
{}
Run Code Online (Sandbox Code Playgroud)

编辑

这同样适用于您的新示例 - 而不是SomeReadOnlyCollection您可以使用的特定内容IEnumerable,因为它是协变的:

void doStuff(params IEnumerable<Base>[] args)
{
    foreach (var list in args)
    {
        foreach (var thing in list)
        {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*ram 6

TDerived需要能够解析为单一类型.在您的示例中,它可以解析的唯一类型是Super,但编译器不会实现这一飞跃.您可以编译器实现这一飞跃.

doStuff(new Super[] { s0, s1 });
doStuff<Super>(s0, s1);
Run Code Online (Sandbox Code Playgroud)

关于您的更新,请考虑(而不是通用方法)定义接受方法IEnumerable<ISuper>,它将支持派生类型,因为它IEnumerable<T>是协变的(从.NET 4开始).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 { }  
Run Code Online (Sandbox Code Playgroud)

IEnumerable<T>由BCL集合因为.NET 2.0实施,包括T[],List<T>,ReadOnlyCollection<T>,HashSet<T>,等.