21

Hi suppose these 2 methods:

private List<IObjectProvider> GetProviderForType(Type type)
        {
            List<IObjectProvider> returnValue = new List<IObjectProvider>();

            foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
            {
                if ((provider.Key.IsAssignableFrom(type) ||
                    type.IsAssignableFrom(provider.Key)) &&
                    provider.Value.SupportsType(type))
                {
                    returnValue.Add(provider.Value);
                }
            }
            return returnValue;
        }

private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
        {
            foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
                if ((provider.Key.IsAssignableFrom(type) ||
                    type.IsAssignableFrom(provider.Key)) &&
                    provider.Value.SupportsType(type))

                    yield return provider.Value;              
        }

Which one is quicker? When I look at the first method, I see that the memory is allocated for List, what in my opinion it's not needed. The IEnumerable method seems to be quicker to me.

For instance, suppose you call

int a = GetProviderForType(myType).Count;
int b = GetProviderForType1(myType).Count();

Now, another issue is, is there a performance difference between these 2 above?

What do you think?

theSpyCry
  • 12,073
  • 28
  • 96
  • 152
  • 16
    The answer to all "which is faster?" questions is the same: Try it both ways. Get out a stopwatch. Then you'll know. – Eric Lippert Jul 31 '09 at 15:30

4 Answers4

37

In this particular case, using the IEnumerable<T> form will be more efficient, because you only need to know the count. There's no point in storing the data, resizing buffers etc if you don't need to.

If you needed to use the results again for any reason, the List<T> form would be more efficient.

Note that both the Count() extension method and the Count property will be efficient for List<T> as the implementation of Count() checks to see if the target sequence implements ICollection<T> and uses the Count property if so.

Another option which should be even more efficient (though only just) would be to call the overload of Count which takes a delegate:

private int GetProviderCount(Type type)
{
  return _objectProviders.Count(provider =>
      (provider.Key.IsAssignableFrom(type) 
       || type.IsAssignableFrom(provider.Key))
      && provider.Value.SupportsType(type));
}

That will avoid the extra level of indirections incurred by the Where and Select clauses.

(As Marc says, for small amounts of data the performance differences will probably be negligible anyway.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think you should be returning an int in this case – bruno conde Jul 31 '09 at 09:29
  • 2
    All of your answers have really helped me, but I think this one has everything mentioned, so I accept it. Thank you! – theSpyCry Jul 31 '09 at 09:34
  • Sorry Jon. Assume that non-empty `Books` is of type either `IEnumerable` or `List` and I only need to read and never modify (add, remove, insert, etc) the `Books`. Which one is more efficient, `Books[0].Author` or `Books.FirstOrDefault().Author`? – Second Person Shooter Jun 13 '19 at 08:41
  • @MoneyOrientedProgrammer: Well `Books[0].Author` won't compile if it's an `IEnumerable`. With a `List` I'd use `Books[0].Author` as it's probably more readable. It's definitely odd to use `FirstOrDefault()` but then not handle the case when it returns `null` - dereferencing to get the author would throw an exception. – Jon Skeet Jun 13 '19 at 09:15
  • Yes. Of course the preliminary assumption are `Books[0].Author` for **non-empty** `Books` of type `List` and `Books.FirstOrDefault().Author` for **non-empty** `Books` of type `IEnumerable`. No resizing is performed is the additional assumption. – Second Person Shooter Jun 13 '19 at 11:06
5

An important part of this question is "how big is the data"? How many rows...

For small amounts of data, list is fine - it will take negligible time to allocate a big enough list, and it won't resize many times (none, if you can tell it how big to be in advance).

However, this doesn't scale to huge data volumes; it seems unlikely that your provider supports thousands of interfaces, so I wouldn't say it is necessary to go to this model - but it won't hurt hugely.

Of course, you can use LINQ, too:

return from provider in _objectProviders
       where provider.Key.IsAssignableFrom(type) ...
       select provider.Value;

This is also the deferred yield approach under the covers...

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

The precise answer to questions like this can vary depending on a lot of factors, and may change further as the CLR evolves. The only way to be sure is to measure it - and bear in mind that if the difference is small compared to the operation this will appear in, then you should pick the most readable, maintainable way of writing it.

And on that note, you might also want to try:

private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
{
    return _objectProviders.Where(provider => 
                  provider.Key.IsAssignableFrom(type) ||
                  type.IsAssignableFrom(provider.Key)) &&
                  provider.Value.SupportsType(type))
                           .Select(p => p.Value);
}

You can also give yourself a lot of flexibility by returning IEnumerable<T> and then using the ToList extension method if you want to "snapshot" the results into a list. This will avoid repeated evaluation of the code to generate the list, if you need to examine it multiple times.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
2

The Main Difference Between IEnumerable and IList:

IEnumerable: Implements MoveNext,Reset,Get Current Methods and Returns a Type of IEnumerator to Iterate Through Records.

IList : Exposes the IEnumerable Interface as well as it is also a collection of non-generic objects which can accessed through index so IEnumerable+ICollection(Manipulation of data) and add,remove,Insert(at specific Index) are the useful methods implemented by IList.

After looking at your Code In My Opinion IEnumerable is more efficient but returning list is also useful if you want to do some manipulation with data and if you just want to Iterate through data then IEnumerable is preferable.

row1
  • 5,568
  • 3
  • 46
  • 72
Vishal Patwardhan
  • 317
  • 1
  • 4
  • 15
  • 1
    Wrong. IEnumerable has just one method. Returning an iterator is substantially more efficient that building a list. – SLaks Jan 22 '12 at 02:36
  • 1
    Another possibility would be to return a type which implements `ICollection` (non-generic!) in read-only fashion as well as `IEnumerable` and possibly `ICollection`. If a class which implements `IEnumerable` also implements `ICollection` or `ICollection`, the `Count` extension method for `IEnumerable` will use the `Count` method of one of those interfaces. Otherwise it will have to enumerate all the items to get a count. Note that the non-generic form is slightly more useful for this purpose because... – supercat Jan 23 '12 at 18:30
  • 1
    ...an `IEnumeration` may be used as an `IEnumeration`, but an `ICollection` cannot be used as an `ICollection`. If a routine which expects an `IEnumeration` were given a class instance which implements `ICollection` but not the non-generic `ICollection`, it would have no way of knowing that the collection should be cast to `ICollection` to get the count. If the class implements non-generic `ICollection`, however, the code which expects an `IEnumerable` would have no trouble finding that. – supercat Jan 23 '12 at 18:33