Neden bazı C # lambda ifadeleri statik yöntemlerle derlenir?


122

Aşağıdaki kodda da görebileceğiniz gibi, bir Action<>nesneyi değişken olarak tanımladım.

Bu eylem yöntemi temsilcisinin neden statik bir yöntem gibi davrandığını lütfen bana bildirin.

Neden trueaşağıdaki kodda geri dönüyor ?

Kod:

public static void Main(string[] args)
{
    Action<string> actionMethod = s => { Console.WriteLine("My Name is " + s); };

    Console.WriteLine(actionMethod.Method.IsStatic);

    Console.Read();
}

Çıktı:

örnek çıktı

Yanıtlar:


153

Bunun nedeni büyük olasılıkla kapanış olmamasıdır, örneğin:

int age = 25;
Action<string> withClosure = s => Console.WriteLine("My name is {0} and I am {1} years old", s, age);
Action<string> withoutClosure = s => Console.WriteLine("My name is {0}", s);
Console.WriteLine(withClosure.Method.IsStatic);
Console.WriteLine(withoutClosure.Method.IsStatic);

Bu, falseiçin withClosureve trueiçin çıktı verecektir withoutClosure.

Bir lambda ifadesi kullandığınızda, derleyici yönteminizi içerecek küçük bir sınıf oluşturur, bu, aşağıdaki gibi bir şeye derlenir (gerçek uygulama büyük olasılıkla biraz değişir):

private class <Main>b__0
{
    public int age;
    public void withClosure(string s)
    {
        Console.WriteLine("My name is {0} and I am {1} years old", s, age)
    }
}

private static class <Main>b__1
{
    public static void withoutClosure(string s)
    {
        Console.WriteLine("My name is {0}", s)
    }
}

public static void Main()
{
    var b__0 = new <Main>b__0();
    b__0.age = 25;
    Action<string> withClosure = b__0.withClosure;
    Action<string> withoutClosure = <Main>b__1.withoutClosure;
    Console.WriteLine(withClosure.Method.IsStatic);
    Console.WriteLine(withoutClosure.Method.IsStatic);
}

Ortaya çıkan Action<string>örneklerin aslında bu oluşturulan sınıflardaki yöntemlere işaret ettiğini görebilirsiniz .


4
+1. Onaylayabilir - bir sonuç olmadan, staticyöntemler için mükemmel adaylar .
Simon Whitehead

3
Ben sadece bu sorunun biraz genişletilmesi gerektiğini önerecektim, geri döndüm ve işte oradaydı. Çok bilgilendirici - derleyicinin kapakların altında ne yaptığını görmek harika.
Liath

4
@Liath Ildasm, gerçekte neler olup bittiğini anlamak için gerçekten yararlı, küçük örnekleri incelemek için ILsekmesini kullanma eğilimindeyim LINQPad.
Lukazoid

@Lukazoid Bu derleyici çıktısını nasıl elde ettiğinizi bize anlatır mısınız? ILDASM böyle bir çıktı vermez .. Herhangi bir araç veya yazılımla mı?
nunu

8
@nunu Bu örnekte, ILsekmesini kullandım LINQPadve C # sonucunu çıkardım . Derlenen çıktının gerçek C # eşdeğerini elde etmek için bazı seçenekler , derlenmiş derlemeyi kullanmak ILSpyveya kullanmak Reflectorolacaktır; büyük olasılıkla, derleyici tarafından üretilen sınıfları değil, lambdaları görüntülemeye çalışacak bazı seçenekleri devre dışı bırakmanız gerekir.
Lukazoid

20

"Eylem yöntemi", yalnızca uygulamanın bir yan etkisi olarak statiktir. Bu, yakalanan değişkenlerin olmadığı anonim bir yöntem durumudur. Yakalanan değişken olmadığından, yöntemin genel olarak yerel değişkenler için olanların dışında ek yaşam süresi gereksinimleri yoktur. Diğer yerel değişkenlere referans verdiyse, ömrü bu diğer değişkenlerin yaşam süresine kadar uzanır (bkz. Bölüm L.1.7, Yerel değişkenler ve bölüm N.15.5.1, Yakalanan dış değişkenler , C # 5.0 belirtiminde).

C # belirtiminin, "anonim sınıflara" değil, yalnızca "ifade ağaçlarına" dönüştürülen anonim yöntemlerden bahsettiğine dikkat edin. İfade ağacı, örneğin Microsoft derleyicisinde ek C # sınıfları olarak temsil edilebilirken, bu uygulama gerekli değildir (C # 5.0 belirtiminde bölüm M.5.3 tarafından onaylandığı gibi). Bu nedenle, anonim işlevin statik olup olmadığı tanımlanmamıştır. Ayrıca K.6 bölümü ifade ağaçlarının detaylarına çok açık bırakmaktadır.


2
+1, belirtilen nedenlerden dolayı bu davranışa büyük olasılıkla güvenilmemelidir; o çok bir uygulama detayıdır.
Lukazoid

18

Delege önbelleğe alma davranışı Roslyn'de değiştirildi. Önceden, belirtildiği gibi, değişkenleri yakalamayan herhangi bir lambda ifadesi staticçağrı sitesinde bir yöntemde derleniyordu . Roslyn bu davranışı değiştirdi. Şimdi, değişkenleri yakalayan veya yakalayan herhangi bir lambda, bir görüntüleme sınıfına dönüştürülür:

Bu örnek verildiğinde:

public class C
{
    public void M()
    {
        var x = 5;
        Action<int> action = y => Console.WriteLine(y);
    }
}

Yerel derleyici çıktısı:

public class C
{
    [CompilerGenerated]
    private static Action<int> CS$<>9__CachedAnonymousMethodDelegate1;
    public void M()
    {
        if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null)
        {
            C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action<int>(C.<M>b__0);
        }
        Action<int> arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1;
    }
    [CompilerGenerated]
    private static void <M>b__0(int y)
    {
        Console.WriteLine(y);
    }
}

Roslyn:

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass0
    {
        public static readonly C.<>c__DisplayClass0 CS$<>9__inst;
        public static Action<int> CS$<>9__CachedAnonymousMethodDelegate2;
        static <>c__DisplayClass0()
        {
            // Note: this type is marked as 'beforefieldinit'.
            C.<>c__DisplayClass0.CS$<>9__inst = new C.<>c__DisplayClass0();
        }
        internal void <M>b__1(int y)
        {
            Console.WriteLine(y);
        }
    }
    public void M()
    {
        Action<int> arg_22_0;
        if (arg_22_0 = C.
                       <>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null)
        {
            C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 =
          new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1);
        }
    }
}

Roslyn'deki temsilci önbelleğe alma davranışı değişiklikleri, bu değişikliğin neden yapıldığından bahsediyor.


2
Teşekkürler, Func <int> f = () => 5 'yöntemimin neden statik olmadığını merak ediyordum
vc 74


1

Yöntemin kapanması yoktur ve ayrıca statik bir yöntemin kendisine (Console.WriteLine) başvurur, bu nedenle statik olmasını beklerim. Yöntem, bir kapatma için çevreleyen anonim bir tür bildirir, ancak bu durumda gerekli değildir.

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.