C # लैंबडा भाव: मुझे उनका उपयोग क्यों करना चाहिए?


309

मैंने Microsoft लैम्ब्डा एक्सप्रेशन प्रलेखन पर जल्दी से पढ़ा है ।

इस तरह के उदाहरण ने मुझे बेहतर समझने में मदद की है, हालाँकि:

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

फिर भी, मुझे समझ नहीं आया कि ऐसा नवाचार क्यों है। यह सिर्फ एक विधि है जो "विधि चर" समाप्त होने पर मर जाती है, है ना? मुझे एक वास्तविक विधि के बजाय इसका उपयोग क्यों करना चाहिए?


3
आप में से जो लोग इस पृष्ठ पर आते हैं और यह नहीं जानते कि delegateC # में क्या है, मैं इस पृष्ठ के बाकी हिस्सों को पढ़ने से पहले इसे पढ़ने का सुझाव देता हूं : stackoverflow.com/questions/2082615/…
Kolob Canyon

जवाबों:


281

अनाम प्रतिनिधियों के लिए लैम्ब्डा अभिव्यक्ति एक सरल वाक्यविन्यास है और हर जगह एक अनाम प्रतिनिधि का उपयोग किया जा सकता है। हालांकि, विपरीत सच नहीं है; lambda अभिव्यक्तियों को अभिव्यक्ति पेड़ों में परिवर्तित किया जा सकता है जो LINQ से SQL जैसे बहुत सारे जादू की अनुमति देता है।

निम्नलिखित अनाम अभिव्यक्ति का उपयोग करने वाले ऑब्जेक्ट अभिव्यक्ति के लिए LINQ का एक उदाहरण है, तो लैम्बडा एक्सप्रेशन यह दिखाने के लिए कि वे आँख पर कितना आसान है:

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

लैम्ब्डा एक्सप्रेशन और अनाम डेलीगेट्स के पास एक अलग फंक्शन लिखने पर एक फायदा है: वे क्लोजर लागू करते हैं जो आपको फंक्शन में मापदंडों को जोड़े बिना या एक बार उपयोग की वस्तुओं को बनाने के लिए फंक्शन में लोकल स्टेट पास करने की अनुमति दे सकता है ।

अभिव्यक्ति के पेड़ C # 3.0 की एक बहुत ही शक्तिशाली नई विशेषता है जो एक एपीआई को केवल एक विधि के संदर्भ में देखने की अनुमति देता है जो कि एक विधि का संदर्भ देता है जिसे निष्पादित किया जा सकता है। एक एपीआई को बस एक डेलिगेट पैरामीटर को एक पैरामीटर में बनाना पड़ता है Expression<T>और कंपाइलर एक अनाम प्रतिनिधि के बजाय एक लैम्ब्डा से एक अभिव्यक्ति ट्री उत्पन्न करेगा:

void Example(Predicate<int> aDelegate);

जैसे कहा जाता है:

Example(x => x > 5);

हो जाता है:

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

उत्तरार्द्ध में अमूर्त वाक्यविन्यास वृक्ष का प्रतिनिधित्व मिलेगा जो अभिव्यक्ति का वर्णन करता है x > 5। LINQ to SQL इस व्यवहार पर निर्भर करता है ताकि सर्वर पर फ़िल्टरिंग / ऑर्डरिंग / आदि के लिए वांछित SQL अभिव्यक्तियों में C # अभिव्यक्तियों को बदल सके।


1
क्लोजर के बिना आप कॉलबैक के रूप में स्थिर तरीकों का उपयोग कर सकते हैं, लेकिन आपको अभी भी उन तरीकों को किसी वर्ग में परिभाषित करना होगा, लगभग सर्टिफिकेट में इस तरह के तरीके का दायरा बढ़ाना है, जो इच्छित उपयोग से परे है।
डी.के.

10
एफडब्ल्यूआईडब्ल्यू, आपके पास एक अनाम प्रतिनिधि के साथ निकटता हो सकती है, इसलिए आपको उसके लिए लैम्ब्डा की सख्त आवश्यकता नहीं है। लैम्ब्डा गुमनाम प्रतिनिधियों की तुलना में केवल अधिक पठनीय हैं, जिसके बिना लिनेक का उपयोग करने से आपकी आंखों से खून निकलेगा।
बेंजोल

138

बेनामी फ़ंक्शंस और भाव एक-बंद तरीकों के लिए उपयोगी होते हैं जो एक पूर्ण विधि बनाने के लिए आवश्यक अतिरिक्त कार्य से लाभ नहीं लेते हैं।

इस उदाहरण पर विचार करें:

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

बनाम

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

ये कार्यात्मक रूप से समकक्ष हैं।


8
इस लैम्ब्डा अभिव्यक्ति को संभालने के लिए फाइंड () विधि को कैसे परिभाषित किया गया होगा?
पैट्रिक डेसजार्डिन्स

3
Predicate <T> वह तरीका है जो ढूँढें विधि अपेक्षा कर रही है।
डैरेन कोप्प

1
चूँकि मेरी लैम्ब्डा एक्सप्रेशन <T> के अनुबंध से मेल खाती है, इसलिए फाइंड () विधि इसे स्वीकार करती है।
जोसेफ Daigle

क्या आपका मतलब था "स्ट्रिंग व्यक्ति = लोग। फ़ाइंड (व्यक्ति => व्यक्ति। कॉन्सर्ट्स (" जो "));"
गर्न ब्लैंस्टन

5
@FKCoder, नहीं, वह नहीं करता है, हालांकि यह स्पष्ट हो सकता है अगर उसने कहा था "स्ट्रिंग व्यक्ति = लोग। पिंड (p => p.Contains (" जो ");"
बेंजोल

84

मैंने उन्हें एक स्थिति में उपयोगी पाया जब मैं किसी नियंत्रण की घटना के लिए एक हैंडलर घोषित करना चाहता था, दूसरे नियंत्रण का उपयोग करना। सामान्य रूप से ऐसा करने के लिए आपको कक्षा के क्षेत्रों में नियंत्रण के संदर्भों को संग्रहीत करना होगा ताकि आप उन्हें एक अलग विधि में उपयोग कर सकें जिससे वे बनाए गए थे।

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

लैम्ब्डा अभिव्यक्ति के लिए धन्यवाद आप इसे इस तरह से उपयोग कर सकते हैं:

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

बहुत आसान।


पहले उदाहरण में, प्रेषक को क्यों न डालें और मूल्य प्राप्त करें?
एंड्रयू

@Andrew: इस सरल उदाहरण में, प्रेषक का उपयोग करना आवश्यक नहीं है, क्योंकि प्रश्न में केवल एक घटक है और फ़ील्ड का उपयोग करने से सीधे एक डाली बचती है, जो स्पष्टता में सुधार करती है। वास्तविक दुनिया के परिदृश्य में, मैं व्यक्तिगत रूप से भेजने वाले का उपयोग करना पसंद करूंगा। आमतौर पर मैं कई घटनाओं के लिए एक ईवेंट हैंडलर का उपयोग करता हूं, यदि संभव हो और इसलिए मुझे वास्तविक प्रेषक की पहचान करनी होगी।
क्रिस टोप्स्की

35

उदाहरण के लिए लैम्ब्डा की सफाई C # 2.0 का गुमनाम प्रतिनिधि वाक्यविन्यास ...

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

इस तरह C # 2.0 में किया गया था:

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

कार्यात्मक रूप से, वे सटीक एक ही काम करते हैं, यह सिर्फ एक बहुत अधिक संक्षिप्त वाक्य रचना है।


3
वे काफी हद तक एक जैसे नहीं हैं - जैसा कि @ नील विलियम्स बताते हैं, आप अभिव्यक्ति वृक्षों का उपयोग करके एक लैम्ब्डास एएसटी निकाल सकते हैं, जबकि अनाम तरीकों का उपयोग नहीं किया जा सकता है।
LJS

यह मेमने के कई अन्य लाभों में से एक है। यह गुमनाम तरीकों से बेहतर कोड को समझने में मदद करता है। निश्चित रूप से यह लैम्ब्डा बनाने का इरादा नहीं है, लेकिन ये ऐसे परिदृश्य हैं जहां इसे अधिक बार उपयोग किया जा सकता है।
गुरुजी

29

यह एक लंबोदर अभिव्यक्ति का उपयोग करने का सिर्फ एक तरीका है। आप एक लैम्बडा अभिव्यक्ति का उपयोग कर सकते हैं कहीं भी आप एक प्रतिनिधि का उपयोग कर सकते हैं। यह आपको इस तरह की चीजें करने की अनुमति देता है:

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");

यह कोड "हैलो" शब्द से मेल खाने वाली प्रविष्टि के लिए सूची खोजेगा। ऐसा करने का दूसरा तरीका यह है कि वास्तव में इस तरह से एक प्रतिनिधि को पास करें।

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

संपादित करें :

C # 2.0 में, यह अनाम प्रतिनिधि सिंटैक्स का उपयोग करके किया जा सकता है:

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

लैम्ब्डा ने उस सिंटैक्स को काफी हद तक साफ किया।


2
@ जोनाथन हॉलैंड: अनाम प्रतिनिधि वाक्यविन्यास को संपादित करने और जोड़ने के लिए धन्यवाद। यह उदाहरण को अच्छी तरह से पूरा करता है।
स्कॉट डॉरमैन

अनाम प्रतिनिधि क्या है? // माफ करना, मैं c # के लिए नया हूँ
HackerMan

1
@HackerMan, एक गुमनाम प्रतिनिधि को एक फ़ंक्शन के रूप में सोचें जिसमें "नाम" नहीं है। आपका अभी भी एक फ़ंक्शन को परिभाषित कर रहा है, जिसमें इनपुट और आउटपुट हो सकते हैं, लेकिन चूंकि यह एक ऐसा नाम है जिसे आप सीधे इसे संदर्भित नहीं कर सकते। ऊपर दिखाए गए कोड में, आप एक विधि को परिभाषित कर रहे हैं (जो एक लेता है stringऔर एक bool) एक पैरामीटर के रूप में Findही विधि के लिए।
स्कॉट डॉर्मन

22

Microsoft ने हमें एक साफ सुथरा तरीका दिया है, जिससे लैंबडा एक्सप्रेशन नाम के गुमनाम डेलीगेट्स तैयार किए जा सकते हैं। हालाँकि, इस कथन के भाव भाग पर बहुत अधिक ध्यान नहीं दिया जा रहा है । Microsoft ने एक संपूर्ण नामस्थान, System.Linq.Expressions जारी किया , जिसमें लैम्ब्डा अभिव्यक्तियों के आधार पर अभिव्यक्ति ट्री बनाने के लिए कक्षाएं शामिल हैं। अभिव्यक्ति के पेड़ उन वस्तुओं से बने होते हैं जो तर्क का प्रतिनिधित्व करते हैं। उदाहरण के लिए, x = y + z एक अभिव्यक्ति है जो .Net में एक अभिव्यक्ति पेड़ का हिस्सा हो सकता है। निम्नलिखित (सरल) उदाहरण पर विचार करें:

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

यह उदाहरण तुच्छ है। और मुझे यकीन है कि आप सोच रहे हैं, "यह बेकार है क्योंकि मैं एक प्रतिनिधि बनाने और बजाय इसे चलाने के बजाय सीधे प्रतिनिधि बना सकता था"। और आप सही होंगे। लेकिन यह अभिव्यक्ति पेड़ों के लिए नींव प्रदान करता है। एक्सप्रेशन नामस्थान में कई अभिव्यक्तियाँ उपलब्ध हैं, और आप अपना स्वयं का निर्माण कर सकते हैं। मुझे लगता है कि आप देख सकते हैं कि यह तब उपयोगी हो सकता है जब आपको पता नहीं हो कि एल्गोरिथ्म डिजाइन या संकलन समय पर क्या होना चाहिए। वैज्ञानिक कैलकुलेटर लिखने के लिए इसका उपयोग करने के लिए मैंने कहीं एक उदाहरण देखा। आप इसे बायेसियन के लिए भी इस्तेमाल कर सकते हैं सिस्टम के लिए, या आनुवंशिक प्रोग्रामिंग के लिए(AI)। मेरे करियर में कुछ समय के लिए मुझे एक्सेल जैसी कार्यक्षमता लिखनी पड़ी, जिससे उपयोगकर्ताओं को उपलब्ध डेटा पर काम करने के लिए सरल भाव (जोड़, घटाव, आदि) दर्ज करने की अनुमति मिली। प्री- नेट 3.5 में, मुझे C # के लिए बाहरी कुछ स्क्रिप्टिंग भाषा का सहारा लेना पड़ा है, या फ़्लाई पर कोड बनाने के लिए प्रतिबिंब में कोड-एमिटिंग कार्यक्षमता का उपयोग करना पड़ा है। अब मैं अभिव्यक्ति पेड़ों का उपयोग करूंगा।


12

यह उन विधियों को होने से बचाता है जो किसी विशिष्ट स्थान पर केवल एक बार उपयोग किए जाने वाले स्थान से दूर परिभाषित किए जाने के लिए उपयोग किए जाते हैं। अच्छे उपयोग जेनेरिक एल्गोरिदम जैसे सॉर्टिंग के लिए तुलना करने वाले होते हैं, जहां आप तब एक कस्टम सॉर्ट फ़ंक्शन को परिभाषित कर सकते हैं, जहां आप आगे कहीं दूर जाने के लिए मजबूर कर रहे हैं, बजाय इसके कि आप कहीं और देख रहे हैं।

और यह वास्तव में एक नवीनता नहीं है। LISP में लगभग 30 वर्ष या उससे अधिक समय से लंबोदर कार्य होते हैं।


6

आप अपने तरीकों पर कार्य करने के लिए जेनेरिक कोड लिखने में लैम्ब्डा अभिव्यक्तियों का उपयोग भी कर सकते हैं।

उदाहरण के लिए: एक जेनेरिक कॉल द्वारा लिए गए समय की गणना करने के लिए सामान्य कार्य। ( Actionयहाँ में)

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

और आप उपरोक्त विधि को लंबोदर अभिव्यक्ति का उपयोग करके कॉल कर सकते हैं,

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

अभिव्यक्ति आपको अपनी पद्धति से रिटर्न मान प्राप्त करने की अनुमति देता है और साथ ही साथ परम को भी

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

5

अनाम विधि का प्रतिनिधित्व करने के लिए लैम्ब्डा अभिव्यक्ति एक संक्षिप्त तरीका है। दोनों अनाम विधियाँ और लैंबडा एक्सप्रेशन आपको विधि कार्यान्वयन इनलाइन को परिभाषित करने की अनुमति देते हैं, हालाँकि, एक अनाम विधि आपको स्पष्ट रूप से पैरामीटर प्रकार और एक विधि के लिए वापसी प्रकार को परिभाषित करने की आवश्यकता है। लैम्ब्डा अभिव्यक्ति C # 3.0 के प्रकार की अनुमान सुविधा का उपयोग करता है जो संकलक को संदर्भ के आधार पर चर के प्रकार का अनुमान लगाने की अनुमति देता है। यह बहुत सुविधाजनक है क्योंकि यह हमें बहुत टाइपिंग बचाता है!


5

एक लंबोदर अभिव्यक्ति एक गुमनाम विधि की तरह एक प्रतिनिधि उदाहरण के रूप में लिखा गया है।

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

लंबोदर अभिव्यक्ति पर विचार करें x => x * x;

इनपुट पैरामीटर मान x (=> के बाईं ओर) है

फ़ंक्शन लॉजिक x * x (दाईं ओर =>) है

एक लैम्ब्डा एक्सप्रेशन का कोड एक एक्सप्रेशन के बजाय एक स्टेटमेंट ब्लॉक हो सकता है।

x => {return x * x;};

उदाहरण

नोट: Funcएक पूर्वनिर्धारित जेनेरिक प्रतिनिधि है।

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

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

संदर्भ

  1. एक प्रतिनिधि और इंटरफ़ेस को परस्पर विनिमय कैसे किया जा सकता है?

4

बहुत बार, आप केवल एक ही स्थान पर कार्यक्षमता का उपयोग कर रहे हैं, इसलिए एक विधि बनाने से सिर्फ कक्षा में गड़बड़ी होती है।


3

यह एक छोटा सा ऑपरेशन करने का तरीका है और इसे जहां इसे इस्तेमाल किया जाता है, उसके बहुत करीब रखा जाता है (इसके उपयोग बिंदु के करीब वैरिएबल घोषित करने के विपरीत नहीं)। यह आपके कोड को अधिक पठनीय बनाने वाला है। अभिव्यक्ति को गुमनाम करके, आप किसी को अपने क्लाइंट कोड को तोड़ने के लिए बहुत कठिन बना रहे हैं यदि यह फ़ंक्शन कहीं और उपयोग किया जाता है और इसे "बढ़ाने" के लिए संशोधित किया जाता है।

इसी तरह, आपको फ़ॉरच का उपयोग करने की आवश्यकता क्यों है? आप लूप के लिए एक मैदान में या सीधे IEnumerable का उपयोग करके सीधे सब कुछ कर सकते हैं। उत्तर: आपको इसकी आवश्यकता नहीं है लेकिन यह आपके कोड को अधिक पठनीय बनाता है।


0

नवाचार प्रकार की सुरक्षा और पारदर्शिता में है। यद्यपि आप प्रकार के लंबोदर अभिव्यक्तियों की घोषणा नहीं करते हैं, वे अनुमानित हैं, और कोड खोज, स्थैतिक विश्लेषण, रीफैक्टरिंग टूल और रनटाइम प्रतिबिंब द्वारा उपयोग किया जा सकता है।

उदाहरण के लिए, इससे पहले कि आप SQL का उपयोग कर सकते हैं और एक SQL इंजेक्शन हमला कर सकते हैं, क्योंकि एक हैकर ने एक स्ट्रिंग पारित की जहां एक संख्या सामान्य रूप से अपेक्षित थी। अब आप एक LINQ लैम्बडा एक्सप्रेशन का उपयोग करेंगे, जो उससे सुरक्षित है।

शुद्ध प्रतिनिधियों पर एक LINQ API का निर्माण संभव नहीं है, क्योंकि उन्हें मूल्यांकन करने से पहले अभिव्यक्ति पेड़ों को एक साथ मिलाने की आवश्यकता होती है।

2016 में अधिकांश लोकप्रिय भाषाओं में लैम्ब्डा अभिव्यक्ति का समर्थन है, और मुख्यधारा की अनिवार्य भाषाओं के बीच C # इस विकास में अग्रणी में से एक था।


0

लैंबडा एक्सप्रेशन का उपयोग करने के लिए यह शायद सबसे अच्छा स्पष्टीकरण है -> https://youtu.be/j9nj5dTT54Q

सारांश में, यह कोड की पठनीयता में सुधार करना है, कोड की प्रतिकृति के बजाय पुन: उपयोग करके त्रुटियों की संभावना को कम करना और पर्दे के पीछे हो रहे अनुकूलन का लाभ उठाना है।


0

लैम्ब्डा एक्सप्रेशन और अनाम फ़ंक्शंस का सबसे बड़ा लाभ यह है कि वे दिए गए लाइब्रेरी / फ्रेमवर्क में कोड के माध्यम से कार्यक्षमता को इंजेक्ट करने के लिए लाइब्रेरी / फ्रेमवर्क के क्लाइंट (प्रोग्रामर) को अनुमति देते हैं (क्योंकि यह LINQ, ASP.NET Core और है कई अन्य) एक तरह से जो नियमित तरीके नहीं कर सकते हैं। हालाँकि, एकल अनुप्रयोग प्रोग्रामर के लिए उनकी ताकत स्पष्ट नहीं है, लेकिन एक पुस्तकालय जो बाद में दूसरों द्वारा उपयोग किया जाएगा जो लाइब्रेरी कोड या लाइब्रेरी का उपयोग करने वाले व्यक्ति को कॉन्फ़िगर करना चाहते हैं। तो लैंबडा अभिव्यक्ति का प्रभावी ढंग से उपयोग करने का संदर्भ एक पुस्तकालय / रूपरेखा का उपयोग / निर्माण है।

इसके अलावा, जब से वे एक बार उपयोग कोड का वर्णन करते हैं, तो उन्हें उस वर्ग का सदस्य होने की आवश्यकता नहीं होती है, जहां अधिक कोड जटिलता हो। हर बार जब हम क्लास ऑब्जेक्ट के संचालन को कॉन्फ़िगर करना चाहते थे, तो अस्पष्ट ध्यान के साथ एक वर्ग घोषित करने की कल्पना करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.