람다 식에서 ref 또는 out 매개 변수를 사용할 수 없습니다


173

람다 식에 ref 또는 out 매개 변수를 사용할 수없는 이유는 무엇입니까?

오늘 오류가 발생하여 해결 방법을 찾았지만 이것이 왜 컴파일 타임 오류인지 궁금합니다.

CS1628 : 익명 메서드, 람다 식 또는 쿼리 식에서 ref 또는 out 매개 변수 'parameter'에서 사용할 수 없습니다.

다음은 간단한 예입니다.

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    int newValue = array.Where(a => a == value).First();
}

그것은 반복자에 관한 것이지만,이 게시물에서 에릭 리퍼 트 ​​(Eric Lippert & mdash; 그는 결국 언어 디자인 팀에 있습니다)와 같은 많은 추론을 람다에 적용합니다 : < blogs.msdn.com/ericlippert/archive/2009/07/13 /… >
Joel Coehoorn

17
찾은 해결 방법이 무엇인지 물어봐도 될까요?
Beatles1692

3
로컬 정규 변수를 선언하고 그 작업을 수행 한 다음 결과를 나중에 값에 할당 할 수 있습니다 ... var tempValue = value; 그런 다음 tempValue로 작업하십시오.
술취한 코드 원숭이

답변:


122

람다는 변수의 수명을 변경하는 것처럼 보입니다. 예를 들어 다음과 람다 식의 파라미터 (P1)가 발생하지 사는 그 값있어서 프레임 이후에 액세스 될 수있는 스택에 더 이상 현재의 방법보다 긴 프레임

Func<int> Example(int p1) {
  return () => p1;
}

캡처 된 변수의 또 다른 속성은 변수에 대한 변경 사항이 람다 식 외부에서도 볼 수 있다는 것입니다. 예를 들어 다음은 42 장입니다.

void Example2(int p1) {
  Action del = () => { p1 = 42; }
  del();
  Console.WriteLine(p1);
}

이 두 속성은 다음과 같은 방법으로 ref 매개 변수의 얼굴을 날리는 특정 효과 세트를 생성합니다.

  • ref 매개 변수는 고정 된 수명을 가질 수 있습니다. 지역 변수를 ref 매개 변수로 함수에 전달하는 것을 고려하십시오.
  • 람다의 부작용은 ref 매개 변수 자체에서 볼 수 있어야합니다. 메소드와 호출자 모두에서.

이들은 다소 호환되지 않는 속성이며 람다 식에서 허용되지 않는 이유 중 하나입니다.


36
나는 우리가 ref람다 식 내부에서 사용할 수는 없다는 것을 이해 하지만 그것을 사용하려는 욕구는 공급되지 않았습니다.
zionpi

85

후드 아래에서 익명 메소드는 캡처 된 변수 (질문 본문이 전부인 것)를 게양 하고이를 컴파일러 생성 클래스의 필드로 저장하여 구현됩니다. ref또는 out매개 변수를 필드로 저장하는 방법은 없습니다 . 에릭 리퍼 (Eric Lippert) 는 블로그 에서이를 언급했다 . 캡처 된 변수와 람다 매개 변수에는 차이가 있습니다. 변수가 캡처되지 않으므로 다음과 같은 "형식 매개 변수"를 가질 수 있습니다 .

delegate void TestDelegate (out int x);
static void Main(string[] args)
{
    TestDelegate testDel = (out int x) => { x = 10; };
    int p;
    testDel(out p);
    Console.WriteLine(p);
}

70

모든 유형을 명시 적으로 정의해야합니다.

(a, b, c, ref d) => {...}

그러나 유효하지 않습니다

(int a, int b, int c, ref int d) => {...}

유효하다


13
그렇습니다; 문제는 왜 당신이 할 수 없는가입니다. 대답은 가능합니다.
Ben Adams

24
그렇지 않습니다. 문제는 이미 정의 된 또는 기존 람다 내부의 변수를 참조 할 수없는 이유 입니다. 예제 코드를 읽으면 분명합니다 (다시 읽으십시오). 받아 들여진 대답은 이유를 명확하게 설명합니다. 당신의 대답은 람다를 사용 하거나 매개 변수 에 관한 것입니다. 질문에 전혀 대답하지 않고 다른 것에 대해 말하기refoutrefout
edc65

4
@ edc65는 옳다 ... 이것은 문제의 주제와 관련이 없다. 이것은 매개 변수 목록 (왼쪽)이 아니라 람바 식 (오른쪽)의 내용에 관한 것입니다. 이것이 26 개의 찬성 투표를 한 것은 기괴하다.
Jim Balter

6
그래도 도움이되었습니다. +1입니다. 감사합니다
Emad

1
그러나 나는 왜 이것이 그렇게 설계되었는지 이해하지 못합니다. 모든 유형을 명시 적으로 정의해야하는 이유는 무엇입니까? 의미 적으로 나는 필요가 없습니다. 내가 잃어버린 것입니까?
joe

5

Google의 "C # lambda ref"에 대한 최상위 결과 중 하나입니다. 위의 답변을 확장해야한다고 생각합니다. 오래된 (C # 2.0) 익명 대리자 구문이 작동하며 더 복잡한 서명 (닫기)을 지원합니다. Lambda 및 익명 대의원은 최소한 컴파일러 백엔드에서 동일하게 인식 된 구현을 공유했으며 가장 중요하게는 클로저를 지원합니다.

검색을 수행하면서 구문을 보여주기 위해 시도한 작업 :

public static ScanOperation<TToken> CreateScanOperation(
    PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
    var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
    return delegate(string text, ref int position, ref PositionInformation currentPosition)
        {
            var token = oldScanOperation(text, ref position, ref currentPosition);
            if (token == null)
                return null;
            if (tokenDefinition.LeftDenotation != null)
                token._led = tokenDefinition.LeftDenotation(token);
            if (tokenDefinition.NullDenotation != null)
                token._nud = tokenDefinition.NullDenotation(token);
            token.Identifier = tokenDefinition.Identifier;
            token.LeftBindingPower = tokenDefinition.LeftBindingPower;
            token.OnInitialize();
            return token;
        };
}

Lambdas는 절차 적으로 수학적으로 안전합니다 (앞서 언급 한 참조 값 승격 때문에). 웜 캔을 열 수 있습니다. 이 구문을 사용할 때는 신중하게 생각하십시오.


3
나는 당신이 그 질문을 오해했다고 생각합니다. 문제는 람다가 컨테이너 메소드에서 ref / out 변수에 액세스 할 수없는 이유가 아니라 람다 자체 가 ref / out 변수를 포함 할 수없는 이유 였습니다. AFAIK 후자의 이유는 없습니다. 오늘은 람다를 작성 (a, b, c, ref d) => {...}하고 ref"는 '심판'키워드로 선언되어야 매개 변수 '4'"오류 메시지와 함께 빨간색 밑줄했다. 페이스 팜! PS "반품 가치 프로모션"이란 무엇입니까?
Qwertie

1
@ Qwertie 나는 전체 매개 변수화, 즉 a, b, c 및 d의 유형을 포함하여 작동하도록 작동했습니다. BenAdams의 답변을 참조하십시오 (원래의 질문도 오해하지만).
Ed Bayiates

@ Qwertie 나는 그 시점의 절반 만 제거했다고 생각합니다. 원래 요점은 ref 매개 변수를 클로저에 배치하는 것이 위험 할 수 있다는 것입니다. 나는 그것이 컴파일 될지 안다).
Jonathan Dickinson

이것은 실제로 묻는 질문과는 아무런 관련이 없습니다 ... 마찬가지로 벤 애덤스 (Ben Adams)의 답변 아래에서 수락 된 답변과 의견을보십시오.
Jim Balter

1

그리고 아마도?

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    var val = value; 
    int newValue = array.Where(a => a == val).First();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.