0

So normally if you want to simply check if an IEnumerable<T> has any items in you'd use .Any() instead of .count > 0 - especially when you're hitting things like LINQ-To-Entities and .count may have a heavy performance penalty.

The problem I have is that I'm writing an IValueConverter to change an objects visibility depending on whether or not an enumerable has items or not:

public class CollectionEmptyVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var col = value as ICollection;

        if (col == null) { return Visibility.Collapsed.ToString(); }

        return (col.Count > 0) ? Visibility.Visible.ToString() : Visibility.Collapsed.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

So in this situation I can't use the normal extension methods on an IEnumerable<T> because I can't have a <T> at this time. My current implementation limits me to things that implement ICollection which may not always be desirable.

How can I do this in a more efficient manner? The bare IEnumerable (sans <T>) doesn't have a .Any().

PhonicUK
  • 13,486
  • 4
  • 43
  • 62

2 Answers2

3

This will work just fine:

public static class ExtensionMethods
{
    public static bool Any(this IEnumerable t) {
        foreach (var o in t)
            return true;
        return false;
    }
}

Benchmarks:

public static class ExtensionMethods
{
    public static bool AnyJR(this IEnumerable t)
    {
        foreach (var o in t)
            return true;
        return false;
    }

    public static bool AnyPhonicUK(this IEnumerable t)
    {
        var e = t.GetEnumerator();
        try {
            return e.MoveNext();
        }
        finally {
            var edisp = e as IDisposable;
            if (edisp != null)
                edisp.Dispose();
        }
    }
}

class Program
{
    static long Test_AnyJR(List<int> l, int n)
    {
        var sw = Stopwatch.StartNew();

        for (int i = 0; i < n; i++) {
            bool any = l.AnyJR();
        }

        sw.Stop();
        return sw.ElapsedMilliseconds;
    }

    static long Test_AnyPhonicUK(List<int> l, int n)
    {
        var sw = Stopwatch.StartNew();

        for (int i = 0; i < n; i++) {
            bool any = l.AnyPhonicUK();
        }

        sw.Stop();
        return sw.ElapsedMilliseconds;
    }

    static void Main(string[] args)
    {
        var list = new List<int> { 1, 2, 3, 4 };

        int N = 10000000;
        Console.WriteLine("{0} iterations using AnyJR took {1} ms.", N, Test_AnyJR(list, N));
        Console.WriteLine("{0} iterations using AnyPhonicUK took {1} ms.", N, Test_AnyPhonicUK(list, N));

        Console.ReadLine();
    }
}

Results:

10000000 iterations using AnyJR took 484 ms.
10000000 iterations using AnyPhonicUK took 393 ms.
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Just after asking I ended up with `col.GetEnumerator().MoveNext()` which works as well - question being which would be faster? Granted it's a pretty close call in either text. Will be marking as correct in either case. – PhonicUK Aug 03 '13 at 23:46
  • 1
    @PhonicUK since enumerators implement `IDisposable`, you shouldn't be doing a command like that. You could wrap it in a using, and return `MoveNext()` to ensure that it is disposed. – Tim S. Aug 03 '13 at 23:52
  • @TimS. I'm confused -- `IEnumerator` is not `IDisposable`. – Jonathon Reinhart Aug 03 '13 at 23:57
  • @PhonicUK Initial benchmarks show your method to be about 65% faster, although Tim S.'s comment has me concerned. I feel like the `foreach` route is just the cleanest, simplest (and probably safest) route. – Jonathon Reinhart Aug 04 '13 at 00:00
  • @JonathonReinhart my mistake: [`IEnumerator`](http://msdn.microsoft.com/en-us/library/78dfe2yb.aspx) implements `IDisposable`. But since you're working with the non-generic version, it starts to get more complicated... – Tim S. Aug 04 '13 at 00:01
  • 1
    And that added complexity is basically this: when you do a `foreach` on an `IEnumerable`, it generates a `finally` block to try to dispose the enumerator, just like when you do it with an `IEnumerable`. The only difference is that it has to check if the object implements `IDisposable` because it might not. – Tim S. Aug 04 '13 at 00:09
  • Now the `MoveNext()` implementation is only 23% faster. I wonder what other "right things" we're not doing that the `foreach` is :-) – Jonathon Reinhart Aug 04 '13 at 00:16
0
public object Convert(object value, Type targetType, object parameter, string language)
{
    var col = value as ICollection;
    if (col != null)
        return col.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
    var enu = (IEnumerable)value;
    foreach (var obj in enu)
        return true;
    return false;
}

Or (more concise, but probably a hair slower):

public object Convert(object value, Type targetType, object parameter, string language)
{
    var enu = (IEnumerable)value;
    return enu.Cast<object>().Any() ? Visibility.Visible : Visibility.Collapsed;
}
Tim S.
  • 55,448
  • 7
  • 96
  • 122