2009-09-02 13 views
125

Dlaczego nie można użyć parametru ref lub out w wyrażeniu lambda?Nie można użyć parametru ref lub out w wyrażeniach lambda

Natrafiłem dzisiaj na błąd i znalazłem obejście, ale wciąż byłem ciekawy, dlaczego jest to błąd podczas kompilacji.

Oto prosty przykład:

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(); 
} 
+10

Mogę zapytać, co było obejście, które miały znaleźć? – Beatles1692

+0

Chodzi o iteratory, ale wiele z tego samego uzasadnienia w tym poście (również Eric Lippert — jest on w końcu w zespole projektowania języka) dotyczy lambdas:

Odpowiedz

94

lambdas mieć wygląd zmienia żywotność zmiennych, które złapią. Na przykład, następujące wyrażenia lambda powoduje P1 parametru na żywych dłuższy niż bieżącej ramki sposób, jak wartość można uzyskać dostęp po wykonaniu ramy sposób już na stosie

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

Inną właściwością przechwyconych zmiennych jest to, że zmiany w zmiennej są również widoczne poza wyrażeniem lambda. Na przykład następujące drukuje 42

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

Te dwie właściwości wytworzyć pewien zestaw efektów, które prowokować parametr ref w następujący sposób

  • parametry ref może mieć ustalony czas życia. Rozważ przekazanie zmiennej lokalnej jako parametru ref funkcji.
  • Efekty uboczne w lambda będą musiały być widoczne na samym parametrze ref. Zarówno w metodzie, jak i w wywołującym.

Są to w pewnym stopniu niezgodne właściwości i są jednym z powodów, dla których nie są dozwolone w wyrażeniach lambda.

+6

Rozumiem, że nie możemy używać wyrażenia "ref" wewnątrz lambda, ale chęć jego użycia nie została podana . – zionpi

66

Pod maską sposób anonimowy jest realizowany przez podnoszące przechwyconych zmienne (czego organizm pytanie wszystkim chodzi) i zapisywania ich jako dziedzinach kompilatora wygenerowana klasa. Nie ma sposobu na zapisanie parametru ref lub out jako pola. Eric Lippert omówił to w a blog entry. Zauważ, że istnieje różnica między przechwyconymi zmiennymi a parametrami lambda. Ci może mają „parametrów formalnych” jak poniżej, ponieważ nie są ujęte zmienne:

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

Ponieważ jest to jeden z najlepszych wyników dla "C# lambda ref" w Google; Uważam, że muszę rozwinąć powyższe odpowiedzi. Starsza (C# 2.0) anonimowa składnia delegatów działa i obsługuje bardziej złożone sygnatury (i zamknięcia). Delegaci Lambdy i anonimowi przynajmniej dzielili się postrzeganą implementacją w zapleczu kompilatora (jeśli nie są identyczni) - i co najważniejsze, obsługują zamknięcia.

Co starałem się robić, kiedy robiłam poszukiwania, w celu wykazania składnię:

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

Wystarczy pamiętać, że lambdas są proceduralnie i matematycznie bezpieczniejsze (ze względu na promocję wartości ref wspomniano wcześniej): możesz otworzyć puszkę z robakami. Zastanów się dobrze, używając tej składni.

+3

Myślę, że źle zrozumiałeś pytanie. Pytanie brzmiało, dlaczego lambda nie mogła uzyskać dostępu do zmiennych ref/out w swojej metodzie kontenera, a nie dlaczego sama _lambda sama nie może zawierać zmiennych ref/out. AFAIK nie ma żadnego powodu dla tego drugiego. Dzisiaj napisałem lambdę '(a, b, c, ref d) => {...}' i 'ref' było czerwone podkreślone z komunikatem o błędzie" Parametr "4" musi zostać zadeklarowany za pomocą słowa kluczowego "ref" ". Facepalm! P.S. czym jest "promocja wartości ref."? – Qwertie

+1

@Qwertie Mam to do pracy z pełną parametryzacją, czyli obejmują typy na, b, c i d i to działa. Zobacz odpowiedź BenAdamsa (chociaż źle zrozumiał pierwotne pytanie). –

+0

@Qwertie Myślę, że usunąłem tylko połowę tego punktu - myślę, że pierwotnym punktem było to, że umieszczenie ref params w zamknięciu może być ryzykowne, ale musiałem potem zdać sobie sprawę, że tak się nie dzieje w przykładzie, który dałem (i ani czy wiem, czy to się skompiluje). –

39

Można, ale trzeba wyraźnie określić wszystkie typy tak

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

jest nieważny, jednak

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

Czy ważna

+3

Chociaż interesujące, to nie odpowiada na pytanie. Spróbuj ponownie przeczytać – edc65

+8

To robi; pytanie brzmi: dlaczego nie możesz; odpowiedź jest możliwa. –

+11

Nie ma; pytanie brzmi: dlaczego nie możesz odwoływać się do * istniejącej zmiennej *, już zdefiniowanej 'ref' lub' out', wewnątrz lambda. Oczywiste jest, że czytasz przykładowy kod (spróbuj ponownie, aby przeczytać go ponownie). Przyjęta odpowiedź jasno wyjaśnia, dlaczego. Twoja odpowiedź dotyczy używania parametru 'ref' lub' out' * * do lambda. Całkowicie nie odpowiadając na pytanie i mówiąc o czymś innym – edc65

Powiązane problemy