Eric Lippert는 반복기 블록 의 한계 (및 이러한 선택에 영향을 미치는 설계 결정)에 대한 훌륭한 기사 시리즈를 작성했습니다.
특히 반복기 블록은 일부 정교한 컴파일러 코드 변환으로 구현됩니다. 이러한 변환은 익명 함수 또는 람다 내부에서 발생하는 변환에 영향을 미쳐 특정 상황에서 둘 다 코드를 다른 구조와 호환되지 않는 다른 구조로 '변환'하려고합니다.
결과적으로 상호 작용이 금지됩니다.
반복자 블록이 내부에서 작동하는 방식은 여기서 잘 다루어 집니다 .
비 호환성의 간단한 예 :
public IList<T> GreaterThan<T>(T t)
{
IList<T> list = GetList<T>();
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item;
}
return items.ToList();
}
컴파일러는 동시에 이것을 다음과 같이 변환하려고합니다.
private class Magic
{
private T t;
private IList<T> list;
private Magic(List<T> list, T t) { this.list = list; this.t = t;}
public IEnumerable<T> DoIt()
{
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item;
}
}
}
public IList<T> GreaterThan<T>(T t)
{
var magic = new Magic(GetList<T>(), t)
var items = magic.DoIt();
return items.ToList();
}
동시에 반복자 측면은 작은 상태 기계를 만드는 작업을 수행하려고합니다. 특정 간단한 예제는 상당한 양의 온 전성 검사 (먼저 (임의로) 중첩 된 클로저를 처리)와 함께 작동 한 다음 맨 아래 수준의 결과 클래스가 반복기 상태 머신으로 변환 될 수 있는지 확인합니다.
그러나 이것은
- 꽤 많은 일.
- 적어도 반복기 블록 측면이 효율성을 위해 특정 변환을 적용하는 것을 방지 할 수있는 반복기 블록 측면 없이는 모든 경우에서 작동 할 수 없습니다 (예 : 완전히 본격적인 폐쇄 클래스가 아닌 인스턴스 변수로 지역 변수를 승격).
- 구현이 불가능하거나 충분히 어려운 부분에서 약간의 중복 가능성이있는 경우 미묘한 변경 사항이 많은 사용자에게 손실되므로 그로 인한 지원 문제의 수가 많을 수 있습니다.
- 매우 쉽게 해결할 수 있습니다.
귀하의 예에서 다음과 같습니다.
public IList<T> Find<T>(Expression<Func<T, bool>> expression)
where T : class, new()
{
return FindInner(expression).ToList();
}
private IEnumerable<T> FindInner<T>(Expression<Func<T, bool>> expression)
where T : class, new()
{
IList<T> list = GetList<T>();
var fun = expression.Compile();
foreach (var item in list)
if (fun.Invoke(item))
yield return item;
}
async
허용하는 익명 람다 를 가질 수 있으므로await
내부에서 익명 반복기를 구현하지 않은 이유를 알고 싶습니다yield
. 다소간 동일한 상태 머신 생성기입니다.