5

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

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

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,请考虑(而不是通用方法)定义一个方法 accepting ,因为它是协变的(从 .NET 4 开始)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>从 .NET 2.0 开始由 BCL 集合实现,包括T[]List<T>ReadOnlyCollection<T>HashSet<T>等。

于 2011-10-20T02:32:05.027 回答
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 回答
0

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

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 回答