views:

362

answers:

9

I've got this:

private IEnumerable _myList;

I need to get a count off of that object. I was previously typing _myList to an array and getting the length, but now we are using this same bit of code with a different kind of object. It's still a Collection type (it's a strongly typed Subsonic Collection object), and everything works great, except for the bit that we need to get the total number of items in the object.

I've tried typing it to CollectionBase, and many many other types, but nothing works that will let me get a .Count or .Length or anything like that.

Can anyone point me in the right direction?

EDIT: I'm not using 3.5, I'm using 2. So, anything dealing with Linq won't work. Sorry for not posting this earlier.

A: 

What about calling .ToList()?

Daniel A. White
Makes a copy of the original list (for a long sequence this would allocate several increasingly large memory blocks, copying the result-so-far each time).
Daniel Earwicker
No need to convert to a list just to get the count - Enumerable.Count() should work just as well.
Reed Copsey
+10  A: 

The System.Linq.Enumerable.Count extension method does this for a typed IEnumerable<T>. For an untyped IEnumerable try making your own extension:

    public static int Count(this IEnumerable source)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }

        ICollection collectionSource = source as ICollection;
        if (collectionSource != null)
        {
            return collectionSource.Count;
        }

        int num = 0;
        IEnumerator enumerator = source.GetEnumerator();
        //try-finally block to ensure Enumerator gets disposed if disposable
        try
        {
            while (enumerator.MoveNext())
            {
                num++;
            }
        }
        finally
        {
            // check for disposal
            IDisposable disposableEnumerator = enumerator as IDisposable;
            if(disposableEnumerator != null)
            {
                disposableEnumerator.Dispose();
            }
        }
        return num;
    }
kek444
Just make sure you include the System.Linq in your using statements to have it available in Intellisense.
LBushkin
He'll also need to redefine to IEnumerable<T> instead of IEnumerable.
Reed Copsey
Count() is only available on IEnumerable<T>, not IEnumerable.
Rex M
Yes, thanks for the correction.
kek444
Don't forget to dispose of the enumerator. (At that point you've basically got my answer :)
Jon Skeet
Thanks Jon, I did it without fianlly and different disposable name to add the illusion I had a say in it :)
kek444
@kek444: It should really be in a try/finally block though.
Jon Skeet
/sigh You're right. And for a moment I thought I could keep some toys for myself. Thanks!
kek444
Thanks for the answer, kek444. I ended up going with the Skeet answer only because you even say in the comments that you should have put it in a try/catch block, which John did. Thanks for the answer though!
Matt Dawdy
No problem, we all learn here :)
kek444
+1  A: 

LINQ provides a Count() extension method.

using System.Linq;

...

var count = _myList.Count();
spoulson
+4  A: 

If you're using .NET 3.5, you can use Enumerable.Count() to get the count from any IEnumerable<T>.

This will not work off a non-generic IEnumerable, though - it requires IEnumerable<T>.

This should work, though, since Subsonic's collection classes implement the appropriate interfaces for you. You'll need to change your definition from IEnumerable to IEnumerable<MyClass>.

Reed Copsey
Yes, why is it declared `IEnumerable` instead of `IEnumerable<T>`?
280Z28
+1  A: 

If you include the System.Linq namespace, IEnumerable<T> has a Count() extension method available. You can write your own extension method to get it on the non-generic version. Note this method will box value types, so if that might end up being a performance concern for you, go with Jon Skeet's solution. This is just simpler.

public static int Count(this IEnumerable enumerable)
{
    int count = 0;
    foreach(object item in enumerable)
    {
        count++;
    }
    return count;
}
Rex M
That boxes everything in the sequence, if it's something like an int[]. There's no need - see my answer for a somewhat more cumbersome but potentially more efficient solution.
Jon Skeet
@Jon good point, thanks.
Rex M
+10  A: 

Is this actually IEnumerable instead of IEnumerable<T>? If so, LINQ won't help you directly. (You can use Cast<T>() as suggested elsewhere, but that will be relatively slow - in particular, it won't be optimised for IList/IList<T> implementations.)

I suggest you write:

public static int Count(this IEnumerable sequence)
{
    if (sequence == null)
    {
        throw new ArgumentNullException("sequence");
    }

    // Optimisation: won't optimise for collections which
    // implement ICollection<T> but not ICollection, admittedly.
    ICollection collection = sequence as ICollection;
    if (collection != null)
    {
        return collection.Count;
    }

    IEnumerator iterator = sequence.GetEnumerator();
    try
    {
        int count = 0;
        while (iterator.MoveNext())
        {
            // Don't bother accessing Current - that might box
            // a value, and we don't need it anyway
            count++;
        }
        return count;
    }
    finally
    {
        IDisposable disposable = iterator as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }
}
Jon Skeet
Any reason for the try/finally block instead of the "using" statement?
Arjan Einbu
Because not all IEnumerators are IDisposables, I'd wager.
Rytmis
@Rytmis is exactly right. IEnumerator<T> extends IDisposable, but IEnumerator doesn't, therefore you can't use it in a using statement. Fun fact: foreach only started disposing of iterators in C# 1.2. In C# 1.0 it didn't. I don't know of a C# 1.1 specification, despite there being .NET 1.1.
Jon Skeet
Thanks. The compiler would see this immediately, I however didn't... ;-)
Arjan Einbu
That worked fantastically. Thanks Mr. Skeet!
Matt Dawdy
+1  A: 

The type you use is IEnumerable, which doesn't have a Count property. But the generic equivalent, IEnumerable(T), has a Count property.

The obvious solution is to use IEnumerable(T), but if you can't, you could do something like this:

_myList.Cast<MyListItemType>().Count()

The cast is an easy way to convert a IEnumerable to an IEnumerable(SomeType) but obviously is not the best way to get the count performance-wise.

If performance is a factor, I'd just loop through the values to get the count, unless you know the underlying collection has a Count property (see Jon Skeet's answer...).

Meta-Knight
A: 

If the underlying object implements ICollection, then you can use the .Count() property.

Larry Watanabe
A: 

I needed the same and I created IEnumerableList. The reason was that I didnt like to evaluate every time I need the count through whole enumerable object as it's done with the extension method Count().

More about it here: http://fknet.wordpress.com/2010/08/11/string-formatwith-extension/

frantisek