문제

나는 이와 같은 일을하는 것이 좋을 것이라고 생각했다 (Lambda가 수익률 수익률을 달성 함) :

public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
    IList<T> list = GetList<T>();
    var fun = expression.Compile();

    var items = () => {
        foreach (var item in list)
            if (fun.Invoke(item))
                yield return item; // This is not allowed by C#
    }

    return items.ToList();
}

그러나 익명 방법으로 수율을 사용할 수 없다는 것을 알았습니다. 왜 궁금합니다. 그만큼 수확 문서 허용되지 않는다고 말하십시오.

허용되지 않았기 때문에 방금 목록을 만들고 항목을 추가했습니다.

도움이 되었습니까?

해결책

에릭 립퍼트 (Eric Lippert)는 최근 일부 경우 수율이 허용되지 않는 이유에 대한 일련의 블로그 게시물을 썼습니다.

edit2 :

  • 7 부 (이것은 나중에 게시되었으며 구체적 으로이 질문을 다룹니다)

당신은 아마 거기에서 답을 찾을 것입니다 ...


edit1 : 이것은 Abhijeet Patel의 의견에 대한 Eric의 답변에서 파트 5의 의견에 설명되어 있습니다.

Q :

에릭

익명의 방법이나 람다 표현 내에서 "수율"이 허용되지 않는 이유에 대한 통찰력을 제공 할 수 있습니까?

ㅏ :

좋은 질문. 익명의 반복자 블록을 갖고 싶습니다. 로컬 변수를 통해 닫힌 작은 시퀀스 생성기를 내내 자신을 구축 할 수 있다는 것은 정말 대단합니다. 간단하지 않은 이유는 다음과 같습니다. 혜택은 비용보다 중요하지 않습니다. 시퀀스 생성기를 현장 내에서 만드는 굉장함은 실제로 사물의 그랜드 체계에서 상당히 작으며 명목 방법은 대부분의 시나리오에서 충분히 작동합니다. 따라서 혜택은 그렇게 설득력이 없습니다.

비용이 큽니다. 반복자 다시 쓰기는 컴파일러에서 가장 복잡한 변환이며 익명 메소드 재 작성이 두 번째로 복잡합니다. 익명의 방법은 다른 익명의 방법 내부에있을 수 있으며 익명의 방법은 반복자 블록 내부에있을 수 있습니다. 따라서 우리가하는 일은 먼저 모든 익명 방법을 다시 작성하여 폐쇄 클래스의 방법이됩니다. 이것은 컴파일러가 방법에 대해 IL을 방출하기 전에 두 번째로 큰 일입니다. 해당 단계가 완료되면 반복자 재 작성자는 반복자 블록에 익명의 방법이 없다고 가정 할 수 있습니다. 그들은 모두 이미 다시 작성되었습니다. 따라서 반복자 재 작성자는 실현되지 않은 익명 방법이있을 수 있다고 걱정하지 않고 반복자를 다시 쓰는 데 집중할 수 있습니다.

또한 반복자 블록은 익명의 방법과 달리 "둥지"되지 않습니다. 반복자 재 작성자는 모든 반복자 블록이 "최상위"라고 가정 할 수 있습니다.

익명의 방법에 반복자 블록이 포함되면이 두 가정 모두 창 밖으로 나옵니다. 익명 메소드가 포함 된 반복자 블록이 포함 된 익명 메소드와 ... Yuck을 포함하는 익명 메소드가 포함 된 반복자 블록을 가질 수 있습니다. 이제 우리는 중첩 반복자 블록과 중첩 익명 메소드를 동시에 처리 할 수있는 다시 쓰기 패스를 작성해야하며 두 개의 가장 복잡한 알고리즘을 훨씬 더 복잡한 알고리즘으로 병합해야합니다. 디자인, 구현 및 테스트하기가 정말 어려울 것입니다. 우리는 그렇게 할만 큼 똑똑합니다. 여기에 스마트 팀이 있습니다. 그러나 우리는 "좋은 것이지만 필요하지 않은"기능에 대한 큰 부담을 받고 싶지 않습니다. - 에릭

다른 팁

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; // This is not allowed by C#
    }

    return items.ToList();
}

컴파일러는 동시에 이것을 다음과 같은 것들로 변환하려고합니다.

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

동시에 반복자 측면은 약간의 상태 기계를 만들기 위해 노력하고 있습니다. 특정 간단한 예는 상당한 양의 정신 점검 (먼저 임의로 중첩 된 폐쇄를 다루는)과 함께 작동 할 수 있습니다. 그런 다음 바닥 수준 결과 클래스가 반복 상태 머신으로 변환 될 수 있는지 확인합니다.

그러나 이것은 될 것입니다

  1. 꽤 많은 일.
  2. 최소한 반복자 블록 측면에서 클로저 측면이 효율에 대한 특정 변환을 적용하는 것을 방지 할 수 없다면 모든 경우에 모든 경우에 작동 할 수는 없습니다 (예 : 완전히 신생 폐쇄 클래스가 아닌 인스턴스 변수로 로컬 변수를 홍보하는 등).
    • 구현하기가 불가능하거나 충분히 어려운 곳에서 약간의 겹칠 가능성이 있었다면, 많은 사용자가 미묘한 파괴 변경이 손실되기 때문에 발생하는 지원 문제의 수가 높아질 것입니다.
  3. 매우 쉽게 일할 수 있습니다.

당신의 예에서 그렇게 :

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

불행히도 나는 그들이 왜 이것을 허용하지 않았는지 모르겠습니다. 물론 이것이 어떻게 작동하는지 상상할 수 있기 때문입니다.

그러나 익명의 방법은 이미이 방법이 기존 클래스의 메소드 또는 심지어 로컬 변수를 다루는 지 여부에 따라 완전히 새로운 클래스로 추출 될 것이라는 점에서 이미 "컴파일러 마술"조각입니다.

또한 반복 방법을 사용합니다 yield 컴파일러 마법을 사용하여 구현됩니다.

내 생각 에이 두 가지 중 하나가 코드를 다른 마법에 대해 식별 할 수 없게 만들고 C# 컴파일러의 현재 버전에 대한이 작업을 수행하는 데 시간을 소비하지 않기로 결정했을 것입니다. 물론, 그것은 전혀 짜증나는 선택이 아닐 수도 있고, 아무도 그것을 구현하려고 생각하지 않았기 때문에 작동하지 않습니다.

100% 정확한 질문을 위해 Microsoft Connect 사이트 및 질문을보고하십시오. 대가로 사용 가능한 것을 얻을 수 있다고 확신합니다.

나는 이것을 할 것이다 :

IList<T> list = GetList<T>();
var fun = expression.Compile();

return list.Where(item => fun.Invoke(item)).ToList();

물론 LINQ 메소드에 대해 .NET 3.5에서 참조 된 System.core.dll이 필요합니다. 그리고 포함 :

using System.Linq;

건배,

교활한

어쩌면 그것은 단지 구문 제한 일 것입니다. C#과 매우 유사한 Visual Basic .NET에서는 글을 쓰는 것이 어색하지만 완벽하게 가능합니다.

Sub Main()
    Console.Write("x: ")
    Dim x = CInt(Console.ReadLine())
    For Each elem In Iterator Function()
                         Dim i = x
                         Do
                             Yield i
                             i += 1
                             x -= 1
                         Loop Until i = x + 20
                     End Function()
        Console.WriteLine($"{elem} to {x}")
    Next
    Console.ReadKey()
End Sub

또한 괄호 안에 주목하십시오 ' here; 람다 기능 Iterator Function...End Function 보고 an IEnumerable(Of Integer) 하지만 아니다 그런 객체 자체. 그 물건을 얻으려면 호출되어야합니다.

1]에 의한 변환 된 코드는 C# 7.3 (CS0149)에서 오류를 일으킨다 :

static void Main()
{
    Console.Write("x: ");
    var x = System.Convert.ToInt32(Console.ReadLine());
    // ERROR: CS0149 - Method name expected 
    foreach (var elem in () =>
    {
        var i = x;
        do
        {
            yield return i;
            i += 1;
            x -= 1;
        }
        while (!i == x + 20);
    }())
        Console.WriteLine($"{elem} to {x}");
    Console.ReadKey();
}

나는 컴파일러가 처리하기가 어렵다는 다른 답변에 주어진 이유에 강력하게 동의하지 않습니다. 그만큼 Iterator Function() vb.net 예제에 Lambda Iterators 용으로 특별히 생성됩니다.

VB에는 다음과 같습니다 Iterator 예어; C# 상대방이 없습니다. IMHO, 이것이 C#의 기능이 아닌 실제 이유는 없습니다.

따라서 실제로 익명의 반복 기능을 원한다면 현재 Visual Basic을 사용하거나 (확인하지 않았습니다) F#을 사용하십시오. 파트 #7 @thomas levesque의 답변에서 (f#의 경우 ctrl+f).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top