Frage

private IEnumerable<string> Tables
{
    get
    {
        yield return "Foo";
        yield return "Bar";
    }
}

Lassen Sie uns sagen, dass ich auf diejenigen iterieren wollen und schreiben etwas wie #n von #m Verarbeitung.

Gibt es eine Möglichkeit ich den Wert von m ohne Iterieren vor meiner Haupt Iteration herausfinden kann?

Ich hoffe, dass ich mich klar.

War es hilfreich?

Lösung

IEnumerable dies nicht unterstützt. Das ist von Entwurf. IEnumerable verwendet lazy evaluation die Elemente, die Sie für nur fragen zu erhalten, bevor Sie sie benötigen.

Wenn Sie die Anzahl der Elemente wissen wollen, ohne über sie iterieren Sie ICollection<T> verwenden können, hat es eine Count Eigenschaft.

Andere Tipps

Die System.Linq.Enumerable.Count Erweiterungsmethode auf IEnumerable<T> hat die folgende Umsetzung:

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

int result = 0;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
    while (enumerator.MoveNext())
        result++;
}
return result;

So ist es versucht, ICollection<T> zu werfen, die eine Count Eigenschaft hat, und verwendet diese, wenn möglich. Andernfalls durchläuft es.

So Ihre beste Wette ist die Count() Erweiterungsmethode auf Ihrem IEnumerable<T> Objekt zu verwenden, wie Sie die beste Leistung erhalten werden möglich, dass Art und Weise.

Hinzufügen nur zusätzliche einige Informationen:

Die Count() Erweiterung nicht immer wiederholen. Betrachten Sie Linq to Sql, wo die Zählung in die Datenbank geht, sondern bringen alle Zeilen zurück, es gibt die Sql Count() Befehl und kehrt die stattdessen zur Folge haben.

Darüber hinaus ist der Compiler (oder Laufzeit) intelligent genug, dass es die Objekte Count() Methode aufrufen, wenn sie eine hat. So ist es nicht als andere Responder sagen, völlig unwissend und Iterieren immer um Elemente zu zählen.

In vielen Fällen, in denen der Programmierer nur if( enumerable.Count != 0 ) Überprüfung wird die Any() Extension-Methode verwendet wird, wie in if( enumerable.Any() ) wesentlich effizienter mit Linq fauler Auswertung ist, wie es Kurzschluss kann, sobald es irgendwelche Elemente bestimmen kann. Es ist auch besser lesbar

Ein Freund von mir hat eine Reihe von Blog-Posts, die für eine Darstellung geben, warum Sie dies nicht tun können. Er schafft Funktion, die eine IEnumerable, wo jede Iteration gibt die nächste Primzahl zurückzukehren, den ganzen Weg ulong.MaxValue, und der nächste Punkt wird nicht berechnet, bis Sie danach fragen. Schnell, Pop Frage: wie viele Elemente zurückgegeben werden

Hier sind die Beiträge, aber sie sind irgendwie lang:

  1. Jenseits Loops (liefert eine anfängliche EnumerableUtility Klasse in den anderen Beiträgen verwendet)
  2. Anwendungen von Iterate (Initial-Implementierung)
  3. verrückt Extention Methoden: ToLazyList (Performance-Optimierungen)

IEnumerable kann nicht ohne Iterieren zählen.

Unter "normalen" Umständen wäre es möglich, für die Klassen der Umsetzung IEnumerable oder IEnumerable , wie List , die Count-Methode zu implementieren, indem Sie die Liste der Rückkehr .Count Eigenschaft. Allerdings ist die Methode Count nicht eigentlich eine Methode auf dem IEnumerable definiert oder IEnumerable-Schnittstelle. (Das einzige, das ist in der Tat ist GetEnumerator.) Und das bedeutet, dass eine klassenspezifische Implementierung kann nicht vorgesehen sein.

Vielmehr Count ist es eine Erweiterungsmethode, auf der statischen Klasse definiert Enumerable. Das bedeutet, kann es auf jeden Fall eines IEnumerable abgeleiteten Klasse aufgerufen werden, unabhängig davon, dass die Klasse der Umsetzung. Aber es bedeutet auch, sie an einem einzigen Ort, außerhalb jeder dieser Klassen implementiert. Was natürlich bedeutet, dass sie in einer Weise umgesetzt werden müssen, die völlig unabhängig von dieser Klasse Interna ist. Die einzige Weise, das Zählen zu tun ist, über Iteration.

Alternativ können Sie wie folgt vor:

Tables.ToList<string>().Count;

Nein, nicht im Allgemeinen. Ein Punkt enumerables in Verwendung ist, dass die tatsächliche Menge von Objekten in der Aufzählung ist nicht bekannt (im Voraus oder sogar überhaupt).

Sie können System.Linq verwenden.

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    private IEnumerable<string> Tables
    {
        get {
             yield return "Foo";
             yield return "Bar";
         }
    }

    static void Main()
    {
        var x = new Test();
        Console.WriteLine(x.Tables.Count());
    }
}

Sie werden das Ergebnis erhalten '2'.

über Ihre unmittelbare Frage gehen (was im negativen gründlich beantwortet wurde), wenn Sie schauen, Fortschritte zu berichten, während der Verarbeitung einen enumerable, könnten Sie in meiner Blog-Post aussehen wollen? Berichterstattung Fortschritt Während Linq Abfragen .

Damit können Sie dies tun:

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (sender, e) =>
      {
          // pretend we have a collection of 
          // items to process
          var items = 1.To(1000);
          items
              .WithProgressReporting(progress => worker.ReportProgress(progress))
              .ForEach(item => Thread.Sleep(10)); // simulate some real work
      };

Ich habe eine solche Art und Weise in einem Verfahren der in IEnumberable Inhalt übergeben zu überprüfen

if( iEnum.Cast<Object>().Count() > 0) 
{

}

In einem Verfahren wie folgt aus:

GetDataTable(IEnumberable iEnum)
{  
    if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further

}

Es hängt davon ab, welche Version von .Net und Umsetzung Ihrer IEnumerable-Objekt. Microsoft die IEnumerable.Count Methode für die Umsetzung zu überprüfen, festgelegt hat, und verwendet die ICollection.Count oder ICollection .Count, Details siehe hier https://connect.microsoft.com/VisualStudio/feedback/details/454130

Und unten ist die MSIL von Ildasm für System.Core, in dem die System.Linq befindet.

.method public hidebysig static int32  Count<TSource>(class 

[mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource> source) cil managed
{
  .custom instance void System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       85 (0x55)
  .maxstack  2
  .locals init (class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource> V_0,
           class [mscorlib]System.Collections.ICollection V_1,
           int32 V_2,
           class [mscorlib]System.Collections.Generic.IEnumerator`1<!!TSource> V_3)
  IL_0000:  ldarg.0
  IL_0001:  brtrue.s   IL_000e
  IL_0003:  ldstr      "source"
  IL_0008:  call       class [mscorlib]System.Exception System.Linq.Error::ArgumentNull(string)
  IL_000d:  throw
  IL_000e:  ldarg.0
  IL_000f:  isinst     class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  brfalse.s  IL_001f
  IL_0018:  ldloc.0
  IL_0019:  callvirt   instance int32 class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>::get_Count()
  IL_001e:  ret
  IL_001f:  ldarg.0
  IL_0020:  isinst     [mscorlib]System.Collections.ICollection
  IL_0025:  stloc.1
  IL_0026:  ldloc.1
  IL_0027:  brfalse.s  IL_0030
  IL_0029:  ldloc.1
  IL_002a:  callvirt   instance int32 [mscorlib]System.Collections.ICollection::get_Count()
  IL_002f:  ret
  IL_0030:  ldc.i4.0
  IL_0031:  stloc.2
  IL_0032:  ldarg.0
  IL_0033:  callvirt   instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource>::GetEnumerator()
  IL_0038:  stloc.3
  .try
  {
    IL_0039:  br.s       IL_003f
    IL_003b:  ldloc.2
    IL_003c:  ldc.i4.1
    IL_003d:  add.ovf
    IL_003e:  stloc.2
    IL_003f:  ldloc.3
    IL_0040:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    IL_0045:  brtrue.s   IL_003b
    IL_0047:  leave.s    IL_0053
  }  // end .try
  finally
  {
    IL_0049:  ldloc.3
    IL_004a:  brfalse.s  IL_0052
    IL_004c:  ldloc.3
    IL_004d:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0052:  endfinally
  }  // end handler
  IL_0053:  ldloc.2
  IL_0054:  ret
} // end of method Enumerable::Count

Hier ist eine große Diskussion über lazy evaluation und verzögerte Ausführung . Grundsätzlich müssen Sie die Liste materialisieren, um diesen Wert zu erhalten.

Ergebnis der IEnumerable.Count () Funktion kann falsch sein. Dies ist ein sehr einfaches Beispiel zu testen:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      var test = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
      var result = test.Split(7);
      int cnt = 0;

      foreach (IEnumerable<int> chunk in result)
      {
        cnt = chunk.Count();
        Console.WriteLine(cnt);
      }
      cnt = result.Count();
      Console.WriteLine(cnt);
      Console.ReadLine();
    }
  }

  static class LinqExt
  {
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkLength)
    {
      if (chunkLength <= 0)
        throw new ArgumentOutOfRangeException("chunkLength", "chunkLength must be greater than 0");

      IEnumerable<T> result = null;
      using (IEnumerator<T> enumerator = source.GetEnumerator())
      {
        while (enumerator.MoveNext())
        {
          result = GetChunk(enumerator, chunkLength);
          yield return result;
        }
      }
    }

    static IEnumerable<T> GetChunk<T>(IEnumerator<T> source, int chunkLength)
    {
      int x = chunkLength;
      do
        yield return source.Current;
      while (--x > 0 && source.MoveNext());
    }
  }
}

Ergebnis muss (7,7,3,3), aber tatsächliches Ergebnis ist (7,7,3,17)

Der einzige Weg, um eine schnelle Zählung zu haben ist, wenn die ursprüngliche Sammlung einen Indexer (wie Array) hat. Um generischen Code mit einer Mindestanforderung erstellen können Sie IEnumerable verwenden, aber wenn Sie die Zählung müssen auch dann ist mein bevorzugter Weg, um diese Schnittstelle zu verwenden:


    public interface IEnumAndCount<out T> : IEnumerable<T>
    {
        int Count { get; }
    }

Wenn Sie Ihre ursprüngliche Sammlung keine Indexer hat, Ihre Count Implementierung über die Auflistung durchlaufen kann, mit dem bekannten Hit in der Leistung O (n).

Wenn Sie nicht etwas ähnliches wie IEnumAndCount verwenden wollen, ist Ihre beste Wette mit Linq.Count aus Gründen von Daniel Earwicker in der Nähe der Spitze dieser Frage gegeben gehen.

Viel Glück!

Nein.

Sehen Sie diese Informationen zur Verfügung überall im Code, den Sie geschrieben haben?

Sie könnten argumentieren, dass der Compiler „sehen“ kann, dass es nur zwei ist, aber das würde bedeuten, dass es zu betrachten jedes Iteratormethode analysieren müßte nur für diesen spezifischen pathologischen Fall. Und selbst wenn es so wäre, wie würden Sie es lesen, die Grenzen eines IEnumerable gegeben?

Ich würde vorschlagen, ToList anrufen. Ja, Sie tun, um die Auszählung zu früh, aber Sie noch Zugriff auf Ihre Liste von Elementen haben.

Es kann die beste Leistung nicht liefern, aber Sie können LINQ verwenden Sie die Elemente in einem IEnumerable zu zählen:

public int GetEnumerableCount(IEnumerable Enumerable)
{
    return (from object Item in Enumerable
            select Item).Count();
}

Ich benutze IEnum<string>.ToArray<string>().Length und es funktioniert gut.

ich einen solchen Code zu verwenden, wenn ich Liste von Strings haben:

((IList<string>)Table).Count
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top