简体   繁体   中英

How to implement ICollection<T> on an IEnumerable<T>

I would like to know how to program what Microsoft is suggesting from the MSDN guidelines for collections , which state the following:

AVOID using ICollection<T> or ICollection as a parameter just to access the
Count property. Instead, consider using IEnumerable<T> or IEnumerable and
dynamically checking whether the object implements ICollection<T> or ICollection.

In short, how do I implement ICollection on an IEnumerable? Microsoft has links all over that article, but no "And here is how you do this" link.

Here is my scenario. I have an MVC web app with a grid that will paginate and have sorting capability on some of the collections. For instance, on an Employee administration screen I display a list of employees in a grid.

Initially I returned the collection as IEnumerable. That was convenient when I didn't need to paginate. But now I'm faced with paginating and needing to extract the Count of employee records to do that. One workaround was to pass an employeeCount integer by ref to my getEmployeeRecords() method and assign the value within that method, but that's just messy.

Based on what I've seen here on StackOverflow, the general recommendation is to use IEnumerable instead of ICollection, or Collection, or IList, or List. So I'm not trying to open up a conversation about that topic. All I want to know is how to make an IEnumerable implement an ICollection, and extract the record count, so my code is more aligned with Microsoft's recommendation. A code sample or clear article demonstrating this would be helpful.

Thanks for your help!

One thing to note is that if you use LINQ's Count() method, it already does the type checking for you:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw Error.ArgumentNull("source");

    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count;

    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count;

    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        checked
        {
            while (e.MoveNext()) count++;
        }
    }

    return count;
}

Initially I returned the collection as IEnumerable.

Well there's half your problem. Return types should be as explicit as possible. If you have a collection, make the return type that collection. (I forget where, but this is mentioned in the guidelines.)

Based on what I've seen here on StackOverflow, the general recommendation is to use IEnumerable instead of ICollection, or Collection, or IList, or List.

Some developers have an obsession with casting everything as IEnumerable. I have no idea why, as there is no guidance anywhere from Microsoft that says that is a good idea. (I do know that some think it somehow makes the return value immutable, but really anyone can cast it back to the base type and make changes to it. Or just use dynamic and never even notice you gave them an IEnumerable.)

That's the rule for return types and local variables. For parameters you should be as accepting as possible. In practice that means accepting either IEnumerable or IList depending on whether or not you need to access it by index.

AVOID using ICollection or ICollection as a parameter just to access the Count property.

The reason for this is that if you need the Count, you probably need to access it by index as well. If not today, then tomorrow. So go ahead and use IList just in case.

(I'm not sure I agree, but it does make some sense.)

In short, how do I implement ICollection on an IEnumerable?

Short answer: the .Count() extension method. Make sure you import System.Linq.

Long answer:

int count = 0;
if (x is ICollection)
    count = ((ICollection)x).Count;
else
    foreach (var c in x)
       count ++;

IEnumerable is an interface, and so is ICollection . It's the object's type that implements one or the other or both. You can check if an object implements ICollection with obj is ICollection .

Example:

public class MyCollection<T> : IEnumerable<T>, ICollection<T>
{
    // ... Implemented methods
}

// ...

void Foo(IEnumerable<int> elements)
{
    int count;
    if (elements is ICollection<int>) {
        count = ((ICollection<int>)elements).Count;
    }
    else {
        // Use Linq to traverse the whole enumerable; less efficient, but correct
        count = elements.Count();
    }
}

// ...

MyCollection<int> myStuff;
Foo(myStuff);

Doesn't ICollection implement IEnumerable already? If you need a collection then you need a collection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM