约束与参数 - 强制收集为参数的方法

本文关键字:参数 方法 约束 | 更新日期: 2023-09-27 18:30:36

我想知道这两种方法之间是否有任何区别。第二个看起来更自然,但这不应该是使用它的唯一原因。也许有一些性能问题或一些恶魔般的曼波詹博与其中任何一个有关?

void FirstMethod<T>(T a) where T : IEnumerable<Animal>
{
    ...
}
void SecondMethod<T>(IEnumerable<T> a) where T : Animal
{
    ...
}

约束与参数 - 强制收集为参数的方法

不同之处在于您可以轻松地将IEnumerable<Dog>传递给第二种方法,但是当你把它传递给第一种方法时,它只会隐式转换为IEnumerable<Animal>

看看小提琴

已编辑 感谢@Servy的评论。

我在 LinqPad 中尝试了此示例:

void Main()
{   
    var cats = new [] { new Cat() };
    FirstMethod(cats);
    SecondMethod(cats);
}
interface Animal
{
}
class Cat : Animal
{
}
void FirstMethod<T>(T a) where T : IEnumerable<Animal>
{
    var b = a.FirstOrDefault();
}
void SecondMethod<T>(IEnumerable<T> a) where T : Animal
{
    var b = a.FirstOrDefault();
}

通过查看生成的 IL 代码,对方法的 2 个调用之间没有区别,它们都接受cats作为参数。

编辑:差异发生在方法中,如下所示。(谢谢@servy的发言)

IL_001D:  ldarg.0     
IL_001E:  ldloc.0     // cats
IL_001F:  call        UserQuery.FirstMethod
IL_0024:  nop         
IL_0025:  ldarg.0     
IL_0026:  ldloc.0     // cats
IL_0027:  call        UserQuery.SecondMethod
FirstMethod:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  box         02 00 00 1B 
IL_0007:  call        System.Linq.Enumerable.FirstOrDefault
IL_000C:  stloc.0     // b
IL_000D:  ret         
SecondMethod:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  call        05 00 00 2B 
IL_0007:  stloc.0     // b
IL_0008:  ret         

在此 IL 代码上使用反编译器会显示装箱发生的位置,以确保类型安全。

private void Main()
{
    Cat[] a = new Cat[1]
    {
        new Cat()
    };
    this.FirstMethod<Cat[]>(a);
    this.SecondMethod<Cat>((IEnumerable<Cat>) a);
}
private void FirstMethod<T>(T a) where T : IEnumerable<Animal>
{
    Enumerable.FirstOrDefault<Animal>((IEnumerable<Animal>) a);
}
private void SecondMethod<T>(IEnumerable<T> a) where T : Animal
{
    Enumerable.FirstOrDefault<T>(a);
}