web-dev-qa-db-fra.com

Calcul du nombre pour IEnumerable (non générique)

Quelqu'un peut-il m'aider avec une méthode d'extension Count pour IEnumerable (interface non générique).

Je sais que ce n'est pas pris en charge dans LINQ mais comment l'écrire manuellement?

46
Homam

La forme la plus simple serait:

public static int Count(this IEnumerable source)
{
    int c = 0;
    using (var e = source.GetEnumerator())
    {
        while (e.MoveNext())
            c++;
    }
    return c;
}

Vous pouvez ensuite améliorer cela en interrogeant ICollection:

public static int Count(this IEnumerable source)
{
    var col = source as ICollection;
    if (col != null)
        return col.Count;

    int c = 0;
    using (var e = source.GetEnumerator())
    {
        while (e.MoveNext())
            c++;
    }
    return c;
}

Mise à jour

Comme Gerard le souligne dans les commentaires, IEnumerable non générique n'hérite pas de IDisposable donc l'instruction normale using ne fonctionnera pas. Il est probablement encore important d'essayer de supprimer ces énumérateurs si possible - une méthode itérateur implémente IEnumerable et peut donc être transmise indirectement à cette méthode Count. En interne, cette méthode itérateur dépendra d'un appel à Dispose pour déclencher ses propres instructions try/finally et using.

Pour faciliter cela dans d'autres circonstances également, vous pouvez créer votre propre version de l'instruction using qui est moins difficile au moment de la compilation:

public static void DynamicUsing(object resource, Action action)
{
    try
    {
        action();
    }
    finally
    {
        IDisposable d = resource as IDisposable;
        if (d != null)
            d.Dispose();
    }
}

Et la méthode Count mise à jour serait alors:

public static int Count(this IEnumerable source) 
{
    var col = source as ICollection; 
    if (col != null)
        return col.Count; 

    int c = 0;
    var e = source.GetEnumerator();
    DynamicUsing(e, () =>
    {
        while (e.MoveNext())
            c++;
    });

    return c;
}
42
Daniel Earwicker
yourEnumerable.Cast<object>().Count()

Au commentaire sur les performances:

Je pense que c'est un bon exemple d'optimisation prématurée, mais voilà:

static class EnumerableExtensions
{
    public static int Count(this IEnumerable source)
    {
        int res = 0;

        foreach (var item in source)
            res++;

        return res;
    }
}
47
Lasse Espeholt

Différents types d'IEnumerable ont différentes méthodes optimales pour déterminer le nombre; malheureusement, il n'existe aucun moyen général de savoir quelle méthode sera la meilleure pour un IEnumerable donné, ni aucun moyen standard par lequel un IEmumerable peut indiquer laquelle des techniques suivantes est la meilleure:

  1. Demandez simplement l'objet directement. Certains types d'objets qui prennent en charge IEnumerable, tels que Array, List et Collection, ont des propriétés qui peuvent signaler directement le nombre d'éléments qu'ils contiennent.
  2. Énumérer tous les éléments, les jeter et compter le nombre d'éléments énumérés.
  3. Énumérer tous les éléments dans une liste, puis utilisez la liste s'il est nécessaire de réutiliser l'énumération.

Chacun de ces éléments sera optimal dans différents cas.

4
supercat

Je pense que le type choisi pour représenter votre séquence d'éléments aurait dû être ICollection au lieu de IEnumerable, en premier lieu.

ICollection et ICollection<T> fournit une propriété Count - plus - chaque ICollection implémente également IEnumearable.

3
Veverke