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

それらを繰り返して、#n of #m を処理するようなものを書きたいとしましょう。

メインの反復の前に反復せずに m の値を見つける方法はありますか?

私は自分自身を明確にしたことを願っています。

4

21 に答える 21

373

IEnumerableこれをサポートしていません。これは仕様によるものです。IEnumerable遅延評価を使用して、必要になる直前に要求した要素を取得します。

使用できる項目を繰り返し処理せずに項目の数を知りたい場合ICollection<T>は、Countプロパティがあります。

于 2008-10-03T21:08:33.977 に答える
248

System.Linq.Enumerable.Count拡張メソッド on にIEnumerable<T>は、次の実装があります。

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;

したがって、プロパティICollection<T>を持つにキャストしようとし、可能であればそれを使用します。Countそれ以外の場合は反復します。

したがって、最善の策はCount()、オブジェクトで拡張メソッドを使用するIEnumerable<T>ことです。これにより、可能な限り最高のパフォーマンスが得られます。

于 2009-05-12T15:48:20.857 に答える
98

いくつかの情報を追加するだけです:

拡張機能は常に繰り返されるCount()わけではありません。Linq to Sql では、カウントがデータベースに送られますが、すべての行を戻す代わりに、SqlCount()コマンドを発行してその結果を返します。

さらに、コンパイラ (またはランタイム) は十分に賢く、オブジェクトCount()メソッドがあればそれを呼び出します。したがって、他のレスポンダーが言うように、完全に無知であり、要素をカウントするために常に反復しているわけではありません。

多くの場合、プログラマーが拡張メソッドif( enumerable.Count != 0 )を使用してチェックしているだけの場合 、要素があると判断できると短絡できるため、linq の遅延評価の方がはるかに効率的です。また、より読みやすくなっていますAny()if( enumerable.Any() )

于 2008-10-04T03:49:04.277 に答える
13

私の友人は、これができない理由を示す一連のブログ投稿を行っています。彼は IEnumerable を返す関数を作成します。この関数では、反復ごとに次の素数が返され、ulong.MaxValue次の項目は要求されるまで計算されません。よくある質問: 返品されたアイテムの数は?

投稿は次のとおりですが、少し長いです。

  1. ループを超えて(他の投稿で使用される初期の EnumerableUtility クラスを提供します)
  2. Iterate の適用(初期実装)
  3. クレイジーな拡張メソッド: ToLazyList (パフォーマンスの最適化)
于 2008-10-03T21:18:39.120 に答える
12

または、次のようにすることもできます。

Tables.ToList<string>().Count;
于 2009-05-12T15:35:35.417 に答える
11

IEnumerableは、反復せずにカウントすることはできません。

「通常の」状況では、List<T>などのIEnumerableまたはIEnumerable<T>を実装するクラスが、List<T>.Countプロパティを返すことによってCountメソッドを実装することが可能です。ただし、Countメソッドは、実際にはIEnumerable<T>またはIEnumerableインターフェイスで定義されたメソッドではありません。(実際には、GetEnumeratorだけです。)これは、クラス固有の実装を提供できないことを意味します。

むしろ、Countは、静的クラスEnumerableで定義された拡張メソッドです。これは、クラスの実装に関係なく、IEnumerable<T>派生クラスの任意のインスタンスで呼び出すことができることを意味します。しかし、それはまた、それらのクラスのいずれかの外部の単一の場所に実装されることを意味します。もちろん、これは、これらのクラスの内部から完全に独立した方法で実装する必要があることを意味します。カウントを行う唯一のそのような方法は、反復を介することです。

于 2008-10-03T21:40:29.683 に答える
9

いいえ、一般的ではありません。列挙型を使用する際の 1 つのポイントは、列挙内のオブジェクトの実際のセットが (事前に、またはまったく) 不明であることです。

于 2008-10-03T21:07:40.233 に答える
8

System.Linq を使用できます。

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

結果「2」が得られます。

于 2012-01-26T19:02:13.763 に答える
5

差し迫った質問 (完全に否定的に回答されています) を超えて、列挙型の処理中に進行状況を報告したい場合は、私のブログ投稿Reporting Progress During Linq Queriesを参照してください。

これを行うことができます:

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
      };
于 2009-05-12T15:56:18.550 に答える
5

メソッド内でそのような方法を使用して、渡されたIEnumberableコンテンツをチェックしました

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

}

このようなメソッド内:

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

}
于 2012-11-02T07:58:29.873 に答える
4

これは、.NetのバージョンとIEnumerableオブジェクトの実装によって異なります。Microsoftは、実装をチェックするためにIEnumerable.Countメソッドを修正し、ICollection.CountまたはICollection<TSource>.Countを使用しています。詳細はこちらhttps://connect.microsoft.com/VisualStudio/feedback/details/454130を参照してください。

以下は、System.Linqが存在するSystem.Core用のIldasmのMSILです。

.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
于 2011-04-25T05:18:23.793 に答える
3

IEnumerable.Count() 関数の結果が間違っている可能性があります。これは、テストする非常に単純なサンプルです。

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

結果は (7,7,3,3) でなければなりませんが、実際の結果は (7,7,3,17) です。

于 2009-08-12T16:41:30.623 に答える
2

ここでは、遅延評価遅延実行に関する素晴らしい議論があります。基本的に、その値を取得するにはリストを具体化する必要があります。

于 2009-05-12T15:41:50.040 に答える
-1

ToList に電話することをお勧めします。はい、列挙を早期に行っていますが、アイテムのリストにはまだアクセスできます。

于 2008-10-04T05:00:20.780 に答える
-1

最高のパフォーマンスが得られない場合がありますが、LINQ を使用して IEnumerable 内の要素をカウントできます。

public int GetEnumerableCount(IEnumerable Enumerable)
{
    return (from object Item in Enumerable
            select Item).Count();
}
于 2013-06-26T08:39:09.027 に答える
-2

私は使用IEnum<string>.ToArray<string>().Lengthしており、正常に動作します。

于 2012-01-18T09:51:48.170 に答える