Yerel işlev vs Lambda C # 7.0


178

C # 7.0 yeni uygulamalara bakıyorum ve yerel işlevleri uyguladıklarını ilginç buluyorum, ancak bir lambda ifadesi yerine yerel bir işlev tercih edilecek bir senaryo hayal edemiyorum ve ikisi arasındaki fark nedir.

Lamdaların anonymousbu arada fonksiyonlar olduğunu anlıyorum , ancak yerel fonksiyonlar değil, ancak yerel fonksiyonun lambda ifadelerine göre avantajları olduğu gerçek bir dünya senaryosu bulamıyorum

Herhangi bir örnek çok takdir edilecektir. Teşekkürler.


9
Jenerikler, çıkış parametreleri, lambda'yı null değerine başlatmak zorunda kalmadan özyinelemeli fonksiyonlar, vb.
Kirk Woll

5
@KirkWoll - Bunu bir cevap olarak göndermelisin.
Enigmativite

Yanıtlar:


276

Bu, yerel işlevlerin ilk kez tartışıldığı C # Tasarım Toplantısı Notlarında Mads Torgersen tarafından açıklandı :

Bir yardımcı fonksiyon istiyorsunuz. Bunu yalnızca tek bir işlevden kullanıyorsunuz ve büyük olasılıkla bu işlevin kapsamındaki değişkenleri ve tür parametrelerini kullanıyor. Öte yandan, bir lambda'nın aksine, birinci sınıf bir nesne olarak buna ihtiyacınız yoktur, bu nedenle ona bir delege türü vermek ve gerçek bir delege nesnesi tahsis etmek önemli değildir. Ayrıca yinelemeli veya genel olmasını veya yineleyici olarak uygulanmasını isteyebilirsiniz.

Üzerinde biraz daha genişlemek için avantajları:

  1. Verim.

    Bir lambda oluştururken, bu durumda gereksiz bir tahsis olan bir temsilci oluşturulmalıdır. Yerel işlevler gerçekten sadece işlevlerdir, delege gerek yoktur.

    Ayrıca, yerel işlevler yerel değişkenleri yakalamada daha verimlidir: lambdalar genellikle değişkenleri bir sınıfa alırken, yerel işlevler refyeniden ayırmayı önleyen bir yapı (kullanarak geçirilir ) kullanabilir .

    Bu aynı zamanda yerel işlevlerin çağrılmasının daha ucuz olduğu ve satır içine alınabileceği anlamına gelir ve muhtemelen performansı daha da artırır.

  2. Yerel işlevler özyinelemeli olabilir.

    Lambdas da özyinelemeli olabilir, ancak önce nullbir delege değişkenine ve sonra lambda'ya atadığınız garip kod gerektirir . Yerel işlevler doğal olarak özyinelemeli olabilir (karşılıklı özyinelemeli dahil).

  3. Yerel işlevler genel olabilir.

    Lambdalar genel olamaz, çünkü somut tipte bir değişkene atanmaları gerekir (bu tip dış kapsamdan genel değişkenler kullanabilir, ancak bu aynı şey değildir).

  4. Yerel işlevler yineleyici olarak uygulanabilir.

    Lambdas, -returning işlevini uygulamak için yield return(ve yield break) anahtar sözcüğünü kullanamaz IEnumerable<T>. Yerel fonksiyonlar yapabilir.

  5. Yerel işlevler daha iyi görünür.

    Bu yukarıdaki alıntıda belirtilmemiş ve sadece kişisel önyargım olabilir, ancak normal işlev sözdiziminin bir delege değişkenine lambda atamaktan daha iyi olduğunu düşünüyorum. Yerel işlevler de daha özlüdür.

    Karşılaştırmak:

    int add(int x, int y) => x + y;
    Func<int, int, int> add = (x, y) => x + y;

22
Yerel işlevlerin arayan tarafında parametre adları olduğunu eklemek istiyorum. Lambdas bilmiyor.
Lensflare

3
@Lensflare Lambdaların parametre adlarının korunmadığı doğrudur, ancak bunun nedeni, kendi adlarına sahip delegelere dönüştürülmeleri gerektiğidir. Örneğin: Func<int, int, int> f = (x, y) => x + y; f(arg1:1, arg2:1);.
svick

1
Harika bir liste! Ancak, IL / JIT derleyicisinin, kullanımları belirli kurallara uyması durumunda delegeler için de 1. maddede belirtilen tüm optimizasyonları nasıl yapabileceğini hayal edebiliyorum.
Marcin Kaczmarek

1
@Kasebash Lambdalar her zaman bir delege kullanırlar ve bu delege kapatmayı bir object. Böylece, lambdalar bir yapı kullanabilir, ancak kutulu olması gerekir, bu yüzden hala bu ek tahsise sahip olursunuz.
svick

1
@happybits Çoğunlukla bir ad vermeniz gerekmediğinde, örneğin yönteme geçtiğinizde olduğu gibi.
svick

83

Svick'in harika cevabına ek olarak , yerel fonksiyonlara bir avantaj daha vardır: İfadeden
sonra bile fonksiyonun herhangi bir yerinde tanımlanabilirler return.

public double DoMath(double a, double b)
{
    var resultA = f(a);
    var resultB = f(b);
    return resultA + resultB;

    double f(double x) => 5 * x + 3;
}

5
Bu gerçekten yararlıdır, çünkü tüm yardımcı işlevleri işlevin #region Helpersen altına yerleştirmeye alışabilirim, bu nedenle bu işlev içindeki karışıklığı önlemek ve ana sınıftaki karışıklığı önlemek.
AustinWBryan

Bunu da takdir ediyorum. Nereye başladığını bulmak için etrafa bakmanıza gerek olmadığından, baktığınız ana işlevi okumayı kolaylaştırır. Uygulama ayrıntılarını görmek istiyorsanız, sonuna bakmaya devam edin.
Remi Despres-Smyth

3
işlevleriniz o kadar büyükse, içinde bölgelere ihtiyaç duyarlarsa, çok büyük olurlar.
ssmith

9

Yerel işlevi nasıl test edeceğinizi merak ediyorsanız , bunu yapmak için işlevselliğe sahip olduğu için JustMock'u kontrol etmelisiniz . Test edilecek basit bir sınıf örneği:

public class Foo // the class under test
{ 
    public int GetResult() 
    { 
        return 100 + GetLocal(); 
        int GetLocal () 
        { 
            return 42; 
        } 
    } 
}

Ve test şu şekilde görünüyor:

[TestClass] 
public class MockLocalFunctions 
{ 
    [TestMethod] 
    public void BasicUsage() 
    { 
        //Arrange 
        var foo = Mock.Create<Foo>(Behavior.CallOriginal); 
        Mock.Local.Function.Arrange<int>(foo, "GetResult", "GetLocal").DoNothing(); 

        //Act 
        var result = foo. GetResult(); 

        //Assert 
        Assert.AreEqual(100, result); 
    } 
} 

İşte JustMock belgelerine bir link .

Yasal Uyarı. JustMock'tan sorumlu geliştiricilerden biriyim .


insanların araçlarını kullanmasını savunan bu kadar tutkulu geliştiricileri görmek harika. Geliştirici araçlarını tam zamanlı bir iş olarak yazmaya nasıl başladınız? Bir Amerikalı olarak benim izlenimim, bir yüksek lisans veya doktora sahibi olmadıkça bu tür kariyerleri bulmanın zor olabileceğidir. comp sci.
John Zabroski

Merhaba John ve nazik sözler için teşekkür ederim. Bir yazılım geliştiricisi olarak, müşterilerim tarafından onlara sunduğum değer için takdir etmekten daha iyi bir şey görmüyorum. Bunu zorlu ve rekabetçi çalışma arzusuyla birleştirdiğinizde, tutkulu olacağım şeylerin oldukça sınırlı bir listesini alacaksınız. Verimlilik geliştirici araçlarını yazmak bu listede. En azından aklımda :) Kariyer ile ilgili olarak, geliştirici araçları sağlayan şirketlerin tüm yazılım şirketlerinin oldukça küçük bir yüzdesi olduğunu düşünüyorum ve bu yüzden böyle bir fırsat bulmak daha zor.
Mihail Vladov

Ayrı bir soru. Neden burada VerifyAll'ı aramıyorsunuz? JustMock'a yerel işlevin çağrıldığını doğrulamasını söylemenin bir yolu var mı?
John Zabroski

2
Merhaba @ JohnZabroski, test edilen senaryo iddiaların ortaya çıkmasını gerektirmedi. Tabii ki, bir çağrı yapıldığını doğrulayabilirsiniz. İlk olarak, yöntemin kaç kez çağrılmasını beklediğinizi belirtmeniz gerekir. Bunun gibi: .DoNothing().OccursOnce();Ve sonra çağrının Mock.Assert(foo);yöntemi çağırarak yapıldığını iddia edin . Diğer senaryoların nasıl desteklendiğini merak ediyorsanız, Onaylama Olayı yardım makalemizi okuyabilirsiniz .
Mihail Vladov

0

Uzun çalışma yöntemleri ile uğraşırken özel olarak çöp toplama basıncını önlemek için satır içi işlevler kullanıyorum. Diyelim ki, belirli bir sembol için 2 yıl veya piyasa verileri almak istersiniz. Ayrıca, gerekirse çok sayıda işlevsellik ve iş mantığı paketlenebilir.

birinin yaptığı şey, sunucuya bir soket bağlantısı açmak ve bir olayı bir olaya bağlayan veriler üzerinde döngü yapmaktır. Bir sınıf tasarlandığı gibi düşünülebilir, sadece bir tanesi gerçekten sadece bir işlevsellik pili için çalışan bir yerde yardımcı yöntemler yazmıyor. Aşağıda bu nasıl görünebilir bazı örnek, ben değişkenleri kullanıyorum ve "yardımcı" yöntemleri sonunda aşağıda olduğunu unutmayın. Sonunda güzelce olay işleyicileri kaldırmak, benim Exchange sınıf harici / enjekte olurdu ben herhangi bir bekleyen olay işleyicisi kayıtlı olmaz

void List<HistoricalData> RequestData(Ticker ticker, TimeSpan timeout)
{
    var socket= new Exchange(ticker);
    bool done=false;
    socket.OnData += _onData;
    socket.OnDone += _onDone;
    var request= NextRequestNr();
    var result = new List<HistoricalData>();
    var start= DateTime.Now;
    socket.RequestHistoricalData(requestId:request:days:1);
    try
    {
      while(!done)
      {   //stop when take to long….
        if((DateTime.Now-start)>timeout)
           break;
      }
      return result;

    }finally
    {
        socket.OnData-=_onData;
        socket.OnDone-= _onDone;
    }


   void _OnData(object sender, HistoricalData data)
   {
       _result.Add(data);
   }
   void _onDone(object sender, EndEventArgs args)
   {
      if(args.ReqId==request )
         done=true;
   } 
}

Aşağıda belirtildiği gibi avantajları görebilirsiniz, burada örnek bir uygulama görebilirsiniz. Umarım faydaları açıklamaya yardımcı olur.


2
1. Bu sadece yerel işlevleri göstermek için gerçekten karmaşık bir örnek ve açıklama. 2. Yerel işlevler, bu örnekte lambdaslarla karşılaştırıldığında herhangi bir ayırmadan kaçınmaz, çünkü yine de delegelere dönüştürülmeleri gerekir. Yani GC'yi nasıl önleyeceklerini anlamıyorum.
svick

1
değişkenleri geçip kopyalamamak, svick'in cevabı gerisini gerçekten iyi bir şekilde kapsar. Cevabını kopyalamaya gerek yok
Walter Vehoeven
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.