이 코드가 주어지면 :
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
yield return item;
}
}
왜 이런 식으로 코딩하지 않아야합니까? :
IEnumerable<object> FilteredList()
{
var list = new List<object>();
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
list.Add(item);
}
return list;
}
yield
키워드 의 기능을 이해합니다 . 컴파일러에게 특정 종류 (반복자)를 만들도록 지시합니다. 그러나 왜 그것을 사용합니까? 코드가 약간 적을뿐 아니라 어떻게해야합니까?
답변
를 사용 yield
하면 수집이 지연됩니다.
처음 5 개 항목 만 필요하다고 가정 해 봅시다. 당신의 방법 은, 처음 다섯 항목을 얻으려면 전체 목록 을 반복해야 합니다. 을 사용 yield
하면 처음 5 개 항목 만 반복합니다.
답변
반복자 블록의 이점은 느리게 작동한다는 것입니다. 따라서 다음과 같은 필터링 방법을 작성할 수 있습니다.
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
한 번에 하나 이상의 항목을 버퍼링하지 않고 원하는만큼 스트림을 필터링 할 수 있습니다. 예를 들어 반환 된 시퀀스의 첫 번째 값만 필요한 경우 모든 것을 새 목록에 복사하려는 이유는 무엇입니까?
다른 예로 반복자 블록을 사용하여 무한 스트림을 쉽게 만들 수 있습니다 . 예를 들어, 다음은 임의의 숫자 순서입니다.
public static IEnumerable<int> RandomSequence(int minInclusive, int maxExclusive)
{
Random rng = new Random();
while (true)
{
yield return rng.Next(minInclusive, maxExclusive);
}
}
목록에 무한 시퀀스를 어떻게 저장 하시겠습니까?
내 Edulinq 블로그 시리즈 는 반복자 블록을 많이 사용 하는 LINQ to Objects의 샘플 구현을 제공합니다 . LINQ는 근본적으로 게으르다. 목록에 넣는 것은 그렇게 작동하지 않는다.
답변
“목록”코드를 사용하면 다음 단계로 넘어 가기 전에 전체 목록을 처리해야합니다. “수율”버전은 처리 된 항목을 즉시 다음 단계로 전달합니다. “다음 단계”에 “.Take (10)”이 포함 된 경우 “수율”버전은 처음 10 개 항목 만 처리하고 나머지는 잊어 버립니다. “목록”코드는 모든 것을 처리했을 것입니다.
이는 많은 처리가 필요하거나 처리해야 할 항목이 많을 때 가장 큰 차이가 있음을 의미합니다.
답변
yield
목록에없는 항목을 반환 하는 데 사용할 수 있습니다 . 다음은 취소 될 때까지 목록을 무한 반복 할 수있는 작은 샘플입니다.
public IEnumerable<int> GetNextNumber()
{
while (true)
{
for (int i = 0; i < 10; i++)
{
yield return i;
}
}
}
public bool Canceled { get; set; }
public void StartCounting()
{
foreach (var number in GetNextNumber())
{
if (this.Canceled) break;
Console.WriteLine(number);
}
}
이것은 쓴다
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
…기타. 취소 될 때까지 콘솔에
답변
object jamesItem = null;
foreach(var item in FilteredList())
{
if (item.Name == "James")
{
jamesItem = item;
break;
}
}
return jamesItem;
위의 코드를 사용하여 FilteredList ()를 반복하고 item.Name == “James”가 목록의 두 번째 항목에 만족되면 사용하는 메소드 yield
가 두 번 생성됩니다. 이것은 게으른 동작입니다.
list를 사용하는 메소드는 n 개의 객체를 모두 목록에 추가하고 전체 목록을 호출 메소드에 전달합니다.
이것은 IEnumerable과 IList의 차이점을 강조 할 수있는 유스 케이스입니다.
답변
내가 사용한 실제 사례 yield
는 피보나치 시퀀스를 계산하는 것입니다.
다음 코드를 고려하십시오.
class Program
{
static void Main(string[] args)
{
Console.WriteLine(string.Join(", ", Fibonacci().Take(10)));
Console.WriteLine(string.Join(", ", Fibonacci().Skip(15).Take(1)));
Console.WriteLine(string.Join(", ", Fibonacci().Skip(10).Take(5)));
Console.WriteLine(string.Join(", ", Fibonacci().Skip(100).Take(1)));
Console.ReadKey();
}
private static IEnumerable<long> Fibonacci()
{
long a = 0;
long b = 1;
while (true)
{
long temp = a;
a = b;
yield return a;
b = temp + b;
}
}
}
이것은 다음을 반환합니다 :
1, 1, 2, 3, 5, 8, 13, 21, 34, 55
987
89, 144, 233, 377, 610
1298777728820984005
Linq 확장을 사용하고 필요한 것만 쿼리 할 수있는 기능을 제공하여 무한 시리즈를 빠르고 쉽게 계산할 수 있기 때문에 좋습니다.
답변
왜 [수율]을 사용합니까? 코드가 약간 적을뿐 아니라 어떻게해야합니까?
때로는 유용하지만 때로는 그렇지 않습니다. 전체 데이터 집합을 검사하고 반환해야하는 경우 오버 헤드가 발생했기 때문에 yield를 사용하면 아무런 이점이 없습니다.
yield가 실제로 빛날 때 부분 집합 만 반환됩니다. 가장 좋은 예는 정렬이라고 생각합니다. 올해의 날짜와 달러 금액이 포함 된 객체 목록이 있고 해당 연도의 첫 소수 레코드를보고 싶다고 가정합니다.
이 작업을 수행하려면 목록을 날짜별로 오름차순으로 정렬 한 다음 처음 5 개를 가져와야합니다. 이 작업이 수율없이 수행 된 경우 마지막 두 날짜가 올바른지 확인하기 위해 전체 목록을 정렬해야합니다.
그러나 수율로 처음 5 개 품목이 설정되면 분류가 중지되고 결과가 제공됩니다. 이것은 많은 시간을 절약 할 수 있습니다.
