Bart Juriewicz
Bart Juriewicz

Reputation: 629

Constraint vs Parameter - way to force collection as parameter

I am wondering if there is any difference between these two methods. Second one look more natural, but that shouldn't be the only reason to use it. Maybe there are some performance issues or some diabolic mambojambo related to any of them?

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

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

Upvotes: 6

Views: 223

Answers (2)

Tok&#39;
Tok&#39;

Reputation: 582

I tried this sample in 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();
}

By looking at the IL code generated, there is no difference between the 2 call to methods and they both accept cats as parameter.

Edit: the differences occur IN the methods as seen below. (thank you @servy for the remark)

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         

Using a decompiler on this IL code shows where the boxing occurs to ensure type safety.

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);
}

Upvotes: 0

Max Brodin
Max Brodin

Reputation: 3938

The difference is that you can easily pass IEnumerable<Dog> to the second method, but when you pass it to the first method it'll just be implicitly converted to an IEnumerable<Animal>

Take a look at the fiddle

Edited Thanks @Servy for comment.

Upvotes: 6

Related Questions