tags:

views:

319

answers:

8

Hi all

I work on applications developed in C#/.NET with Visual Studio and very often Resharper, in the prototypes of my methods, advises me to replace the type of my input parameters with more generic ones. For instance List<> with IEnumerable<> if I only use the list with a foreach in the body of my method. I can understand why it looks smarter to write that but I'm quite concerned with the performance. I fear that the performance of my apps will decrease if I listen to Resharper...

Can someone explain to me precisely (more or less) what's happening behind the scene (ie in the CLR) when I write

public void myMethod(IEnumerable<string> list)
{
  foreach (string s in list)
  {
    Console.WriteLine(s);
  }
}

static void Main()
{
  List<string> list = new List<string>(new string[] {"a", "b", "c"});
  myMethod(list);
}

and what is the difference with

public void myMethod(List<string> list)
{
  foreach (string s in list)
  {
    Console.WriteLine(s);
  }
}

static void Main()
{
  List<string> list = new List<string>(new string[] {"a", "b", "c"});
  myMethod(list);
}
+1  A: 

In general, the increased flexibility will be worth what minor performance difference it would incur.

CookieOfFortune
+1  A: 

The basic reason for this recommendation is creating a method that works on IEnumberable vs. List is future flexibility. If in the future you need to create a MySpecialStringsCollection, you could have it implement the IEnumerable method and still utilize the same method.

Essentially, I think it comes down, unless you're noticing a significant, meaningful performance hit (and I'd be shocked if you noticed any); prefer a more tolerant interface, that will accept more than what you're expecting today.

Timothy Carter
+3  A: 

You'd have to look at the generated code to be certain, but in this case, I doubt there's much difference. The foreach statement always operates on an IEnumerable or IEnumerable<T>. Even if you specify List<T>, it will still have to get the IEnumerable<T> in order to iterate.

John Saunders
Actually the foreach statement *doesn't* require an IEnumerable or IEnumerable<T> to operate. The type just has to have a GetEnumerator() method which returns something with an appropriate MoveNext() method and Current property. But in this case of course List<T> *does* implement IEnumerable<T>.
Jon Skeet
Also, if the user has a better way of doing things if given a List<T> or Collection<T>, they are free to check if the IEnumerable<T> is one of those classes and special case to improve performance.
sixlettervariables
Although they'd usually be better off checking for IList<T> and ICollection<T> :)
Jon Skeet
@Jon: I never knew that. I always thought it used the interface. Do you know of examples of types that have a GetEnumerator() that returns something with a MoveNext/Current, but which is not IEnumerable/IEnumerator?
John Saunders
+1  A: 

In the first version (IEnumerable) it is more generic and actually you say the method accepts any argument that implements this interface.

Second version yo restrict the method to accept sepcific class type and this is not recommended at all. And the performance is mostly the same.

Ahmed Said
+7  A: 

You're worried about performance - but do you have any grounds for that concern? My guess is that you haven't benchmarked the code at all. Always benchmark before replacing readable, clean code with more performant code.

In this case the call to Console.WriteLine will utterly dominate the performance anyway.

While I suspect there may be a theoretical difference in performance between using List<T> and IEnumerable<T> here, I suspect the number of cases where it's significant in real world apps is vanishingly small.

It's not even as if the sequence type is being used for many operations - there's a single call to GetEnumerator() which is declared to return IEnumerator<T> anyway. As the list gets larger, any difference in performance between the two will get even smaller, because it will only have any impact at all at the very start of the loop.

Ignoring the analysis though, the thing to take out of this is to measure performance before you base coding decisions on it.

As for what happens behind the scenes - you'd have to dig into the deep details of exactly what's in the metadata in each case. I suspect that in the case of an interface there's one extra level of redirection, at least in theory - the CLR would have to work out where in the target object's type the vtable for IEnumerable<T> was, and then call into the appropriate method's code. In the case of List<T>, the JIT would know the right offset into the vtable to start with, without the extra lookup. This is just based on my somewhat hazy understanding of JITting, thunking, vtables and how they apply to interfaces. It may well be slightly wrong, but more importantly it's an implementation detail.

Jon Skeet
Indeed -- particularly in real-world apps where if you're doing anything with networks, databases or disks, it's very unlikely that something like this will be your performance problem.
Greg Beech
ok maybe the fact that I give a concrete example hides my real question. And maybe my question was not the good one. Actually I'm more curious about the mechanism of the CLR than performance in itself; Obviously when you write tons of code, the systematic use of most generic types and interface shouldn't be the thing that cost the most.
PierrOz
A: 

An interface simply defines the presence and signature of public methods and properties implemented by the class. Since the interface does not "stand on its own", there should be no performance difference for the method itself, and any "casting" penalty - if any - should be almost too small to measure.

GalacticCowboy
A: 

The definition for List<T> is:

[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>, 
    IEnumerable<T>, IList, ICollection, IEnumerable

So List<T> is derived from IList, ICollection, IList<T>, and ICollection<T>, in addition to IEnumerable and IEnumerable<T>.

The IEnumerable interface exposes the GetEnumerator method which returns an IEnumerator, a MoveNext method, and a Current property. These mechanisms are what the List<T> class uses to iterate through the list with foreach and next.

It follows that, if IList, ICollection, IList<T>, and ICollection<T> are not required to do the job, then it's sensible to use IEnumerable or IEnumerable<T> instead, thereby eliminating the additional plumbing.

Robert Harvey
A: 

There is no performance penalty for a static-upcast. It's a logical construct in program text.

As other people have said, premature optimization is the root of all evil. Write your code, run it through a hotspot analysis before you worry about performance tuning things.