2013-08-03 5 views
0

Так что, если вы хотите просто проверить, есть ли у IEnumerable<T> какие-либо предметы, вы должны использовать .Any() вместо .count > 0 - особенно когда вы нажимаете такие вещи, как LINQ-To-Entities и .count могут иметь большой штраф за выполнение.Проверка того, имеет ли произвольное перечисление какие-либо элементы без генераторов

У меня есть проблема в том, что я пишу IValueConverter, чтобы изменить видимость объектов в зависимости от того, имеет ли или нет перечислимы предметы или нет:

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(); 
    } 
} 

Таким образом, в этой ситуации я не могу использовать нормальные методы расширения на IEnumerable<T>, потому что в настоящее время я не могу иметь <T>. Моя текущая реализация ограничивает меня тем, что реализует ICollection, что может не всегда быть желательным.

Как я могу сделать это более эффективно? Голый IEnumerable (sans <T>) не имеет .Any().

ответ

3

Это будет работать нормально:

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

Ориентиры:

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(); 
    } 
} 

Результаты:

10000000 iterations using AnyJR took 484 ms. 
10000000 iterations using AnyPhonicUK took 393 ms. 
+0

Только спросив я закончил с 'col.GetEnumerator() MoveNext()', который работает так же - вопрос существа, которое было бы быстрее.? Конечно, это довольно близкий вызов в любом тексте. Будут отмечены как правильные в любом случае. – PhonicUK

+1

@PhonicUK, поскольку счетчики реализуют 'IDisposable', вы не должны делать такую ​​команду. Вы можете обернуть его при использовании и вернуть 'MoveNext()', чтобы убедиться, что он удален. –

+0

@TimS. Я смущен - 'IEnumerator' не' IDisposable'. –

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; 
} 

Или (более краткие, но, вероятно, волосы медленнее):

public object Convert(object value, Type targetType, object parameter, string language) 
{ 
    var enu = (IEnumerable)value; 
    return enu.Cast<object>().Any() ? Visibility.Visible : Visibility.Collapsed; 
}