Lambda ifadelerinde ref veya out parametresi kullanılamaz


173

Neden lambda ifadesinde ref veya out parametresini kullanamıyorsunuz?

Bugün hatayla karşılaştım ve bir çözüm buldum ama bunun neden derleme zamanı hatası olduğunu merak ettim.

CS1628 : Anonim bir yöntem, lambda ifadesi veya sorgu ifadesi içinde ref veya out parametresi 'parametre' içinde kullanılamaz

İşte basit bir örnek:

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

Yineleyiciler hakkında, ancak bu yazıdaki aynı mantığın çoğu (ayrıca Eric Lippert & mdash tarafından; sonuçta dil tasarım ekibinde) lambdas için geçerlidir: < blogs.msdn.com/ericlippert/archive/2009/07/13 /… >
Joel Coehoorn

17
Bulduğunuz geçici çözümün ne olduğunu sorabilir miyim?
Beatles1692

3
Sadece yerel bir normal değişken bildirebilir ve bununla çalışabilir ve sonucu daha sonra değere atayabilirsiniz ... var var tempValue = value; ve sonra tempValue ile çalışın.
Sarhoş Kod Maymun

Yanıtlar:


122

Lambda'lar yakaladıkları değişkenlerin ömrünü değiştiriyor gibi görünmektedir. Örneğin, aşağıdaki lambda ifadesi, p1 parametresinin geçerli yöntem çerçevesinden daha uzun süre yaşamasına neden olur, çünkü yöntem çerçevesi artık yığın üzerinde olmadığında değerine erişilebilir

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

Yakalanan değişkenlerin bir başka özelliği de, değişkente yapılan değişikliklerin lambda ifadesinin dışında da görülebilmesidir. Örneğin, aşağıdaki baskılar 42

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

Bu iki özellik, bir ref parametresinin karşısında aşağıdaki şekillerde uçan belirli bir efekt kümesi üretir

  • ref parametrelerinin sabit bir ömrü olabilir. Bir işleve yerel bir değişkeni ref parametresi olarak geçirmeyi düşünün.
  • Lambdadaki yan etkilerin ref parametresinin kendisinde görünmesi gerekir. Hem yöntem içinde hem de arayanda.

Bunlar biraz uyumsuz özelliklerdir ve lambda ifadelerinde izin verilmemelerinin nedenlerinden biridir.


36
Anladım ki reflambda ifadesinin içinde kullanamayız , ama onu kullanma arzusu beslenmedi.
zionpi

85

Başlık altında, anonim yöntem, yakalanan değişkenlerin kaldırılması (soru bedeninizin temelini oluşturur) ve bunları derleyici tarafından oluşturulan bir sınıfın alanları olarak depolayarak uygulanır. A refveya outparametresini alan olarak depolamanın bir yolu yoktur . Eric Lippert bunu bir blog girişinde tartıştı . Yakalanan değişkenler ile lambda parametreleri arasında bir fark olduğunu unutmayın. Sen olabilir onlar yakalanan değişkenler olarak aşağıdaki gibi "resmi parametreleri" bulunmaktadır:

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

Yapabilirsiniz ancak tüm türleri açıkça tanımlamanız gerekir.

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

Ancak geçersiz

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

Geçerli


13
Öyle; soru neden yapamıyorsunuz; cevap olabilir.
Ben Adams

24
Öyle değil; soru , zaten tanımlanmış veya bir lambda içinde mevcut bir değişkene neden başvuramamanızdır. Örnek kodu okuduğunuz açıktır (tekrar okumayı tekrar deneyin). Kabul edilen cevap nedenini açık bir şekilde açıklıyor. Cevabınız lambda için veya parametresini kullanmakla ilgilidir . Tamamen soruyu cevaplamıyor ve başka bir şey hakkında konuşmarefoutrefout
edc65

4
@ edc65 haklı ... bunun sorunun konusu ile ilgili değildir, bu da lamba ifadesinin (sağda), parametre listesiyle (solda) içeriği ile ilgilidir. Bunun 26 vesile olması tuhaf.
Jim Balter

6
Yine de bana yardımcı oldu. Bunun için +1. Teşekkürler
Emad

1
Ama neden böyle olmak için tasarlandığını hala anlamıyorum. Neden tüm türleri açıkça tanımlamam gerekiyor? Anlamsal olarak buna ihtiyacım yok. Bir şey mi kaybediyorum?
joe

5

Bu, Google'daki "C # lambda ref" için en iyi sonuçlardan biri olduğu için; Yukarıdaki cevapları genişletmem gerektiğini hissediyorum. Eski (C # 2.0) anonim delege sözdizimi çalışır ve daha karmaşık imzaları (kapanışları da) destekler. Lambda ve anonim delegeler en azından derleyici arka ucunda algılanan uygulamayı paylaştılar (aynı değilse) - ve en önemlisi, kapanışları destekliyorlar.

Sözdizimini göstermek için, arama yaptığımda ne yapmaya çalışıyordum:

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'ın prosedürel ve matematiksel olarak daha güvenli olduğunu unutmayın (daha önce bahsedilen ref değeri promosyonu nedeniyle): bir kutu solucan açabilirsiniz. Bu sözdizimini kullanırken dikkatlice düşünün.


3
Bence soruyu yanlış anladın. Soru, lambda'nın neden ref / out değişkenlerine kapsayıcı yönteminde erişemediği, lambda'nın neden ref / out değişkenlerini içeremediği değil. AFAIK ikincisi için iyi bir neden yok. Bugün bir lambda yazdım (a, b, c, ref d) => {...}ve ref" 'ref' anahtar kelimesi ile beyan edilmesi gerekir Parametre '4'" hata iletisiyle kırmızı-altı çizildi. Facepalm! PS "ref değeri promosyonu" nedir?
Qwertie

1
@Qwertie Bunu tam parametrelendirme ile çalışmak için aldım, yani a, b, c ve d türlerini içerir ve çalışır. BenAdams'ın cevabına bakın (orijinal soruyu da yanlış anlamasına rağmen).
Ed Bayiates

@Qwertie Sanırım bu noktanın sadece yarısını kaldırdım - asıl nokta, ref parametrelerini bir kapatmaya yerleştirmenin riskli olabileceğini düşünüyorum, ancak daha sonra bunun verdiğim örnekte gerçekleşmediğini (ve Bunun derlenip derlenmeyeceğini biliyorum).
Jonathan Dickinson

Bunun aslında sorulan soru ile bir ilgisi yok ... kabul edilen cevabı ve aynı şekilde soruyu yanlış anlayan Ben Adams'ın cevabının altındaki yorumları görün.
Jim Balter

1

Ve belki de bu?

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();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.