답변:
IEnumerable
이것을 지원하지 않습니다. 이것은 의도적으로 설계된 동작입니다. IEnumerable
지연 평가를 사용하여 요청하기 전에 필요한 요소를 가져옵니다.
당신은 당신이 사용할 수있는 그들을 반복하지 않고 항목의 수를 알고 싶은 경우에 ICollection<T>
, 그것은이 Count
속성을.
System.Linq.Enumerable.Count
확장 방법에 대한 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>
하는 것입니다.
ICollection<T>
먼저 캐스팅하려고하는 것이 매우 흥미 롭습니다 .
T
에 IEnumerator<T> enumerator = source.GetEnumerator()
: 단순히 당신이 할 수 IEnumerator enumerator = source.GetEnumerator()
와 작동합니다!
IEnumerable<T>
상속 IDisposable
수 있습니다 using
문은 자동으로 처리합니다. IEnumerable
하지 않습니다. 당신이 호출하면 그래서 GetEnumerator
어느 쪽이든, 당신은으로 마무리한다var d = e as IDisposable; if (d != null) d.Dispose();
추가 정보를 추가하기 만하면됩니다.
Count()
확장은 항상 반복하지 않습니다. Linq to Sql을 고려하십시오. 여기서 카운트는 데이터베이스로 이동하지만 모든 행을 다시 가져 오는 대신 Sql Count()
명령을 실행하고 대신 결과를 반환합니다.
또한 컴파일러 (또는 런타임)는 객체 Count()
메소드가있는 경우 객체 메소드를 호출 할 정도로 똑똑 합니다. 그래서 그건 하지 다른 조치가 완전히 무식한 항상 요소를 계산하기 위해 반복, 말하는 것처럼.
프로그래머가 확장 방법을 if( enumerable.Count != 0 )
사용하여 검사 하는 경우가 많지만 Any()
, if( enumerable.Any() )
linq의 지연 평가에서는 요소가 있으면 단락 될 수 있기 때문에 훨씬 효율적 이기 때문에 확장 방법을 사용하여 검사 하는 경우가 많습니다 . 더 읽기 쉽습니다
.Count
경우 항상 크기를 알고 있으므로 속성 을 사용하십시오 . collection.Count
추가 계산이없는 쿼리 는 이미 알려진 카운트를 반환합니다. Array.length
내가 아는 한 동일합니다 . 그러나를 .Any()
사용하여 소스의 열거자를 가져오고 가능한 using (IEnumerator<TSource> enumerator = source.GetEnumerator())
경우 true를 리턴합니다 enumerator.MoveNext()
. 컬렉션 : if(collection.Count > 0)
, 배열 : if(array.length > 0)
및 열거 형의 경우 if(collection.Any())
.
IQueryably<T>
A를 IEnumerable<T>
그것은 SQL 카운트를 발행하지 않습니다. 내가 이것을 쓸 때 Linq to Sql은 새로운 것이 었습니다. Any()
열거 형, 컬렉션 및 SQL의 경우 훨씬 효율적이고 읽을 수 있기 때문에 사용하려고 추진하고 있다고 생각 합니다. 답변을 개선해 주셔서 감사합니다.
내 친구에게 왜 그렇게 할 수 없는지를 보여주는 일련의 블로그 게시물이 있습니다. 그는 각 반복이 다음 소수를 리턴하는 IEnumerable을 리턴하는 함수를 작성 ulong.MaxValue
하며, 다음 항목은 요청할 때까지 계산되지 않습니다. 빠르고 대중적인 질문 : 몇 개의 품목이 반환됩니까?
게시물은 다음과 같지만 길다.
EnumerableFeatures
객체를 반환하도록 함 )을 포함하는 것은 열거 자에게 어려운 일을 요구할 필요는 없지만 그러한 질문을 할 수있는 것입니다. 항상 같은 순서의 항목을 반환합니다. ","기본 컬렉션을 변경하지 않아야하는 코드에 안전하게 노출시킬 수 있습니까? "등)은 매우 유용합니다.
set yield options
은이를 뒷받침 하는 진술이나 유사한 정보를 얻을 수 있습니다. 적절하게 설계된 경우 , 특히 IEnhancedEnumerator
"방어 적" ToArray
또는 ToList
전화를 많이 제거하여 LINQ와 같은 것들을 훨씬 더 유용하게 만들 수 있습니다 .
Enumerable.Concat
자신에 대해 많은 것을 알고있는 큰 컬렉션과 그렇지 않은 작은 컬렉션을 결합하는 데 사용되는 경우 .
IEnumerable은 반복없이 계산할 수 없습니다.
"정상"환경에서는 List <T>와 같이 IEnumerable 또는 IEnumerable <T>을 구현하는 클래스가 List <T> .Count 속성을 반환하여 Count 메서드를 구현할 수 있습니다. 그러나 Count 메서드는 실제로 IEnumerable <T> 또는 IEnumerable 인터페이스에 정의 된 메서드가 아닙니다. (실제로 유일한 것은 GetEnumerator입니다.) 이는 클래스 별 구현을 제공 할 수 없음을 의미합니다.
오히려 Count 메서드는 정적 클래스 Enumerable에 정의 된 확장 메서드입니다. 이는 해당 클래스의 구현에 관계없이 IEnumerable <T> 파생 클래스의 모든 인스턴스에서 호출 될 수 있음을 의미합니다. 그러나 이는 해당 클래스 외부의 단일 위치에서 구현됨을 의미합니다. 물론이 클래스의 내부와 완전히 독립적 인 방식으로 구현되어야 함을 의미합니다. 계산하는 유일한 방법은 반복을 통한 것입니다.
또는 다음을 수행 할 수 있습니다.
Tables.ToList<string>().Count;
아니요, 일반적으로 아닙니다. 열거 형을 사용할 때 한 가지 요점은 열거의 실제 개체 집합을 알 수 없다는 것입니다 (사전 또는 전혀 아님).
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'입니다.
열거 형을 처리하는 동안 진행 상황을보고하려는 경우 즉각적인 질문 (음수로 철저하게 대답 됨)을 넘어 서면 내 블로그 게시물 인 Linq 쿼리 중 진행 상황을보고 싶을 수 있습니다 .
이를 통해 다음을 수행 할 수 있습니다.
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
};
전달 된 IEnumberable
내용 을 확인하기 위해 메소드 내부에서 이러한 방법을 사용했습니다.
if( iEnum.Cast<Object>().Count() > 0)
{
}
다음과 같은 방법으로 :
GetDataTable(IEnumberable iEnum)
{
if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further
}
bool hasContents = false; if (iEnum != null) foreach (object ob in iEnum) { hasContents = true; ... your code per ob ... }
.
.Net의 버전과 IEnumerable 객체의 구현에 따라 다릅니다. Microsoft는 IEnumerable.Count 메서드를 수정하여 구현을 확인했으며 ICollection.Count 또는 ICollection <TSource> .Count를 사용합니다. 자세한 내용은 여기를 참조 하십시오 https://connect.microsoft.com/VisualStudio/feedback/details/454130
아래는 System.Linq가있는 Ildasm의 System.Core 용 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
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)입니다
최상의 성능을 얻을 수는 없지만 LINQ를 사용하여 IEnumerable의 요소를 계산할 수 있습니다.
public int GetEnumerableCount(IEnumerable Enumerable)
{
return (from object Item in Enumerable
select Item).Count();
}
나는 사용 IEnum<string>.ToArray<string>().Length
하고 잘 작동합니다.
IEnum<string>.Count();
?