Thomas
Thomas

Reputation: 2984

Does usage of contains on IEnumerable cast it to a List?

I'm using Linq to filter Data I get from the database. Due to design choices made 1 method returns me an IEnumerable<int> which I then use for a linq statement to see which IDs are permitted to be returned (code follows below). My question here is as I'm not seeing anything there in the documentation: Does the Contains method implicitly cast the IEnumerable to a List for the statement to be executed? (If so the question is if using List in the first place instead of IEnumerable is better).

Code Example

private List<MyData> GetAllPermittedData()
{
    IEnumerable<int> permitteddIds = GetPermittedIDs();
    return (from a in MyDataHandler.GetAllData() where permittedIds.Contains(a.Id)
            select a);
}

Like I asked above I'm not sure if the Contains part implicitly converts permittedIds into a List<int> (for/inside the use of the Contains statement). If this is the case then a followup question would be if it is not better to already use the following statement instead (performance-wise):

private List<MyData> GetAllPermittedData()
{
    List<int> permitteddIds = GetPermittedIDs().ToList();
    return (from a in MyDataHandler.GetAllData() where permittedIds.Contains(a.Id)
            select a);
}

Upvotes: 0

Views: 979

Answers (3)

O. R. Mapper
O. R. Mapper

Reputation: 20780

The Contains method may try to cast the passed IEnumerable<T> to IList<T> or to ICollection<T>. If the cast succeeds, it may directly use the methods of IList<T>, otherwise it will enumerate over the full sequence.

Note that I am writing may because this is implementation-specific and it is not specified in the docs. As such, it could be different across .NET versions and also in alternative implementations such as Mono.

Your advantage by providing only an IEnumerable<T> is that you have more freedom to exchange the object returned from that property without changing the public interface. The performance cost of the attempted cast to IList<T> or similar should be negligible.

In any case, this way is more performant than your suggestion of calling ToList, as that will actually create a new List<T> and copy all items from the enumeration into it.

Upvotes: 0

vcsjones
vcsjones

Reputation: 141703

The LINQ operator will attempt to cast it to ICollection<T> first. If the cast succeeds, it uses that method. Since List<T> implements this interface, it will use the list's contain method.

Note that if you use the overload that accepts an IEqualityComparer, it must iterate over the enumerable and the ICollection shortcut is not taken.

You can see this implementation in the .NET Framework reference source:

public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value) {
        ICollection<TSource> collection = source as ICollection<TSource>;
        if (collection != null) return collection.Contains(value);
        return Contains<TSource>(source, value, null);
}

Jon Skeet also has a good (and lengthy) blog series called "Reimplementing LINQ" where he discusses the implementation in depth. He specifically covers Contains in part 32 of his blog.

Upvotes: 3

BCdotWEB
BCdotWEB

Reputation: 1048

Contains exists as an extension method for IEnumerable<T>. But you con't need to convert your IEnumerable to a List<T> with ToList(), you could simply use that IEnumerable<T> to fill a HashSet<T>:

var permitteddIds = new HashSet<int>(GetPermittedIDs());

Upvotes: -1

Related Questions