C # Lambda ifadeleri: Neden bunları kullanmalıyım?


309

Hızlı bir şekilde okudum Microsoft Lambda İfade belgelerini .

Bu tür bir örnek daha iyi anlamama yardımcı oldu:

delegate int del(int i);
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25

Yine de neden böyle bir yenilik olduğunu anlamıyorum. Bu sadece "yöntem değişkeni" sona erdiğinde ölen bir yöntem, değil mi? Bunu gerçek bir yöntem yerine neden kullanmalıyım?


3
Bu sayfaya gelen ve delegateC # 'da ne olduğunu bilmeyenler için, bu sayfanın geri kalanını okumadan önce bunu okumanızı şiddetle tavsiye ederim : stackoverflow.com/questions/2082615/…
Kolob Kanyonu

Yanıtlar:


281

Lambda ifadeleri , anonim delegeler için daha basit bir sözdizimidir ve anonim bir delegenin kullanılabileceği her yerde kullanılabilir. Ancak bunun tersi doğru değildir; lambda ifadeleri, LINQ to SQL gibi bir sürü büyüye izin veren ifade ağaçlarına dönüştürülebilir.

Aşağıda anonim delegeler ve daha sonra göz üzerinde ne kadar kolay olduklarını göstermek için lambda ifadeleri kullanan bir LINQ to Objects ifadesi örneği verilmiştir :

// anonymous delegate
var evens = Enumerable
                .Range(1, 100)
                .Where(delegate(int x) { return (x % 2) == 0; })
                .ToList();

// lambda expression
var evens = Enumerable
                .Range(1, 100)
                .Where(x => (x % 2) == 0)
                .ToList();

Lambda ifadeleri ve anonim delegelerin ayrı bir işlev yazmaya göre bir avantajları vardır: işleve parametreler eklemeden veya bir kerelik kullanım nesneleri oluşturmadan işleve yerel durumu geçirmenize olanak tanıyan kapaklar uygularlar .

İfade ağaçları , bir API'nin yalnızca çalıştırılabilecek bir yönteme başvurmak yerine bir ifadenin yapısına bakmasına izin veren çok güçlü bir yeni özelliktir. Bir API'nin bir Expression<T>parametreye bir temsilci parametresi yapması gerekir ve derleyici anonim bir temsilci yerine lambda'dan bir ifade ağacı oluşturur:

void Example(Predicate<int> aDelegate);

şöyle denir:

Example(x => x > 5);

dönüşür:

void Example(Expression<Predicate<int>> expressionTree);

İkincisi , ifadeyi tanımlayan soyut sözdizimi ağacının bir temsilini geçecektir x > 5. LINQ to SQL, C # ifadelerini sunucu tarafında filtreleme / sıralama / vb. İçin istenen SQL ifadelerine dönüştürebilmek için bu davranışa dayanır.


1
Kapanışlar olmadan geri çağrı olarak statik yöntemleri kullanabilirsiniz, ancak yine de bu yöntemleri bir sınıfta tanımlamanız gerekir, bu tür yöntemin kapsamını neredeyse amaçlanan kullanımın ötesinde artırır.
DK.

10
FWIW, anonim bir delege ile kapanışlara sahip olabilirsiniz , bu yüzden bunun için kesinlikle lambdalara ihtiyacınız yoktur. Lambdas, Linq'i kullanmanın gözlerinizi kanamasına neden olacak anonim delegelerden çok daha okunaklı.
Benjol

138

Anonim işlevler ve ifadeler, tam bir yöntem oluşturmak için gereken ekstra çalışmalardan faydalanmayan tek seferlik yöntemler için yararlıdır.

Bu örneği düşünün:

 string person = people.Find(person => person.Contains("Joe"));

karşı

 public string FindPerson(string nameContains, List<string> persons)
 {
     foreach (string person in persons)
         if (person.Contains(nameContains))
             return person;
     return null;
 }

Bunlar işlevsel olarak eşdeğerdir.


8
Bu lambda ifadesini işlemek için Find () yöntemi nasıl tanımlanırdı?
Patrick Desjardins

3
<T>, Find yönteminin beklediği şeydir.
Darren Kopp

1
Lambda ifadem Predicate <T> sözleşmesiyle eşleştiğinden, Find () yöntemi bunu kabul eder.
Joseph Daigle

"string person = people.Find (persons => persons.Contains (" Joe "));"
Gern Blanston

5
@FKCoder, hayır, hayır, ama "string person = people.Find (p => p.Contains (" Joe "));"
Benjol

84

Onları, başka bir denetim kullanarak, bir denetimin olayı için bir işleyici bildirmek istediğim bir durumda yararlı buldum. Normal olarak yapmak için, denetimlerin referanslarını oluşturulduklarından farklı bir yöntemde kullanabilmeniz için sınıfın alanlarına depolamanız gerekir.

private ComboBox combo;
private Label label;

public CreateControls()
{
    combo = new ComboBox();
    label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged);
}

void combo_SelectedIndexChanged(object sender, EventArgs e)
{
    label.Text = combo.SelectedValue;
}

lambda ifadeleri sayesinde bunu şöyle kullanabilirsiniz:

public CreateControls()
{
    ComboBox combo = new ComboBox();
    Label label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;};
}

Daha kolay.


İlk örnekte, neden göndereni yayınlayıp değeri elde etmiyorsunuz?
Andrew

@Andrew: Bu basit örnekte göndereni kullanmak gerekli değildir, çünkü söz konusu yalnızca bir bileşen vardır ve alanı kullanmak doğrudan netliği artıran bir döküm kaydeder. Gerçek dünya senaryosunda, şahsen ben de göndereni kullanmayı tercih ederim. Genellikle mümkünse birkaç olay için bir olay işleyici kullanırım ve bu nedenle asıl göndereni tanımlamam gerekir.
Chris Tophski

35

Lambda C # 2.0'ın anonim delege sözdizimini temizledi ... örneğin

Strings.Find(s => s == "hello");

C # 2.0'da böyle yapıldı:

Strings.Find(delegate(String s) { return s == "hello"; });

İşlevsel olarak, aynı şeyi yaparlar, sadece çok daha özlü bir sözdizimi.


3
Değiller oldukça aynı şey - @Neil Williams işaret ettiği gibi anonim yöntemleri aynı şekilde kullanılamaz oysa siz, ifade ağaçlar kullanılarak Lambda'lar AST çıkarabilir.
ljs

bu lambda'nın diğer birçok avantajından biridir. Kodu anonim yöntemlerden daha iyi anlamaya yardımcı olur. şüphesiz bu lambdalar yaratma niyeti değildir ama bunlar daha sık kullanılabileceği bir senaryodur.
Guruji

29

Bu lambda ifadesini kullanmanın sadece bir yoludur. Temsilci kullanabileceğiniz her yerde lambda ifadesi kullanabilirsiniz. Bu, bunun gibi şeyler yapmanızı sağlar:

List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

strings.Find(s => s == "hello");

Bu kod, listede "merhaba" kelimesiyle eşleşen bir giriş arar. Bunu yapmanın diğer bir yolu da, Find yöntemine bir temsilci iletmektir:

List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

private static bool FindHello(String s)
{
    return s == "hello";
}

strings.Find(FindHello);

DÜZENLE :

C # 2.0, bu anonim temsilci sözdizimi kullanılarak yapılabilir:

  strings.Find(delegate(String s) { return s == "hello"; });

Lambda bu sözdizimini önemli ölçüde temizledi.


2
@Jonathan Holland: Anonim delege sözdizimini düzenlediğiniz ve eklediğiniz için teşekkür ederiz. Örneği güzel bir şekilde tamamlıyor.
Scott Dorman

anonim delege nedir? // üzgünüm c # için
yeniyim

1
@HackerMan, anonim bir temsilciyi "adı" olmayan bir işlev olarak düşünün. Hala giriş ve çıkışa sahip olabilen bir işlevi tanımlarsınız, ancak bir isim olduğu için doğrudan ona başvuramazsınız. Yukarıda gösterilen kodda, bir yöntemi (a alır stringve a döndürür bool), Findyöntemin kendisine parametre olarak tanımlarsınız .
Scott Dorman

22

Microsoft bize Lambda ifadeleri adı verilen anonim delegeler oluşturmanın daha temiz ve daha kolay bir yolunu verdi. Ancak, bu ifadenin ifadeler bölümüne fazla dikkat edilmemektedir . Microsoft , lambda ifadelerine dayalı ifade ağaçları oluşturmak için sınıflar içeren System.Linq.Expressions adının tamamını yayımladı . İfade ağaçları mantığı temsil eden nesnelerden oluşur. Örneğin, x = y + z, .Net içindeki bir ifade ağacının parçası olabilecek bir ifadedir. Aşağıdaki (basit) örneği düşünün:

using System;
using System.Linq;
using System.Linq.Expressions;


namespace ExpressionTreeThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int>> expr = (x) => x + 1; //this is not a delegate, but an object
            var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime
            Console.WriteLine(del(5)); //we are just invoking a delegate at this point
            Console.ReadKey();
        }
    }
}

Bu örnek önemsizdir. Ve eminim ki, "Bu, bir ifade oluşturmak ve çalışma zamanında derlemek yerine doğrudan delege oluşturabildiğim için işe yaramaz." Ve haklı olurdun. Ancak bu, ifade ağaçlarının temelini oluşturur. İfadeler ad alanlarında birkaç ifade vardır ve kendi ifadelerinizi oluşturabilirsiniz. Sanırım algoritmanın tasarım veya derleme sırasında tam olarak ne olması gerektiğini bilmediğinizde bunun yararlı olabileceğini görebilirsiniz. Bunu bilimsel bir hesap makinesi yazmak için kullandığım bir yerde bir örnek gördüm. Bayesian için de kullanabilirsiniz sistemleri veya genetik programlama(Al). Kariyerimde birkaç kez, kullanıcıların mevcut veriler üzerinde çalışabilmesi için basit ifadeler (toplama, alt bölümler, vb.) Girmesine izin veren Excel benzeri işlevsellik yazmak zorunda kaldım. Pre..Net 3.5 içinde C # dışında bazı komut dosyası dili başvurmak zorunda kaldı ya da anında kod yayan işlevselliği yansıması .Net kodu oluşturmak için kullanmak zorunda kaldı. Şimdi ifade ağaçlarını kullanırdım.


12

Belirli bir yerde sadece bir kez kullanılan yöntemlerin, kullandıkları yerden çok uzakta tanımlanmasına gerek kalmadan tasarruf sağlar. İyi kullanımlar, sıralama gibi genel algoritmaların karşılaştırıcılarıdır; burada, sıralama yaptığınızı görmek için başka bir yere bakmaya zorlamak yerine, sıralamayı çağırdığınız özel bir sıralama işlevi tanımlayabilirsiniz.

Ve bu gerçekten bir yenilik değil. LISP yaklaşık 30 yıldır lambda fonksiyonlarına sahiptir.


6

Lambda ifadelerinin kullanımını, yöntemlerinize göre genel kodlar yazarken de bulabilirsiniz.

Örneğin: Bir yöntem çağrısı tarafından geçen süreyi hesaplayan genel işlev. (yani Actionburada)

public static long Measure(Action action)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

Ve lambda ifadesini kullanarak yukarıdaki yöntemi aşağıdaki gibi çağırabilirsiniz,

var timeTaken = Measure(() => yourMethod(param));

İfade, yönteminizden dönüş değeri almanıza ve parametrenin dışına çıkmanıza olanak tanır

var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam));

5

Lambda ifadesi anonim bir yöntemi temsil etmenin kısa bir yoludur. Hem anonim yöntemler hem de Lambda ifadeleri, yöntem uygulamasını yerinde tanımlamanızı sağlar, ancak anonim bir yöntem, açıkça bir yöntem için parametre türlerini ve dönüş türünü tanımlamanızı gerektirir. Lambda ifadesi, derleyicinin bağlama göre değişkenin türünü çıkarmasını sağlayan C # 3.0'ın tür çıkarım özelliğini kullanır. Bu çok kullanışlı çünkü bu bize çok fazla yazarak tasarruf sağlıyor!


5

Lambda ifadesi, temsilci örneği yerine yazılan anonim bir yöntem gibidir.

delegate int MyDelagate (int i);
MyDelagate delSquareFunction = x => x * x;

Lambda ifadesini düşünün x => x * x;

Giriş parametre değeri x'dir (=> öğesinin sol tarafında)

İşlev mantığı x * x'dir (=> öğesinin sağ tarafında)

Lambda ifadesinin kodu, ifade yerine bir ifade bloğu olabilir.

x => {return x * x;};

Misal

Not: Funcönceden tanımlanmış bir genel delegedir.

    Console.WriteLine(MyMethod(x => "Hi " + x));

    public static string MyMethod(Func<string, string> strategy)
    {
        return strategy("Lijo").ToString();
    }

Referanslar

  1. Bir temsilci ve arabirim birbirinin yerine nasıl kullanılabilir?

4

Çoğu zaman, işlevselliği sadece tek bir yerde kullanıyorsunuz, bu yüzden bir yöntem yapmak sadece sınıfı tıkıyor.


3

Küçük bir işlem yapmanın ve kullanıldığı yere çok yakın bir şekilde koymanın bir yoludur (kullanım noktasına yakın bir değişken bildirmekten farklı olarak). Bu, kodunuzu daha okunabilir hale getirmelidir. İfadeyi anonimleştirerek, işlev başka bir yerde kullanılırsa ve "geliştirmek" için değiştirilirse, birisinin istemci kodunuzu kırmasını çok daha zor hale getirirsiniz.

Benzer şekilde, neden foreach kullanmanız gerekiyor? Her şeyi foreach içinde bir döngü için düz ile veya sadece doğrudan IEnumerable kullanarak yapabilirsiniz. Cevap: İhtiyacınız yok ama kodunuzu daha okunaklı hale getiriyor.


0

Yenilik, güvenlik ve şeffaflık türündedir. Lambda ifadelerinin türlerini bildirmemenize rağmen, bunlar çıkarılır ve kod arama, statik analiz, yeniden düzenleme araçları ve çalışma zamanı yansıması tarafından kullanılabilir.

Örneğin, bir hacker normalde bir sayının beklendiği bir dizeyi geçtiği için SQL kullanmış ve bir SQL enjeksiyon saldırısı alabilirsiniz. Şimdi bundan korunan bir LINQ lambda ifadesi kullanırsınız.

Saf delegeler üzerinde bir LINQ API oluşturmak mümkün değildir, çünkü bunları değerlendirmeden önce ifade ağaçlarını bir araya getirmeyi gerektirir.

2016'da popüler dillerin çoğunun lambda ifade desteği vardır ve C #, ana akım zorunlu diller arasında bu evrimin öncülerinden biriydi.


0

Bu, lambda ifadelerinin neden kullanılacağıyla ilgili belki de en iyi açıklamalar -> https://youtu.be/j9nj5dTo54Q

Özetle, kod okunabilirliğini artırmak, kodu çoğaltmak yerine yeniden kullanarak hata olasılığını azaltmak ve sahne arkasında gerçekleşen optimizasyondan faydalanmaktır.


0

Lambda ifadelerinin ve anonim işlevlerin en büyük yararı, bir kütüphane / çerçevenin istemcisine (programcı) verilen kütüphane / çerçevedeki kod aracılığıyla işlevselliği enjekte etmelerine izin vermesidir (LINQ, ASP.NET Core ve diğerleri) normal yöntemlerin yapamayacağı şekilde. Bununla birlikte, güçleri tek bir uygulama programcısı için değil, daha sonra kitaplık kodunun davranışını veya kitaplıkları kullanan kişinin yapılandırmasını isteyecek başkaları tarafından kullanılacak kitaplıkları oluşturan kitap için açıktır. Dolayısıyla, lambda ifadesini etkin bir şekilde kullanma bağlamı bir kütüphane / çerçevenin kullanımı / oluşturulmasıdır.

Ayrıca bir kerelik kullanım kodunu tanımladıkları için, daha fazla kod karmaşıklığına yol açacak bir sınıfın üyesi olmaları gerekmez. Bir sınıf nesnesinin işleyişini her yapılandırmak istediğimizde, net olmayan bir sınıf tanımlamak zorunda olduğunuzu düşünün.

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.