c# - आप Func<T> के बजाय अभिव्यक्ति<Func<T>> का उपयोग क्यों करेंगे?




delegates lambda (6)

Krzysztof Cwalina की पुस्तक ( फ्रेमवर्क डिजाइन दिशानिर्देश: सम्मेलन, मुहावरे, और पुन: प्रयोज्य .NET पुस्तकालयों के लिए पैटर्न ) से इसके बारे में एक और दार्शनिक स्पष्टीकरण है;

गैर-छवि संस्करण के लिए संपादित करें:

अधिकांश बार आप फनक या एक्शन चाहते हैं यदि कुछ ऐसा करने की ज़रूरत है तो कुछ कोड चलाने के लिए है। जब कोड को चलाने से पहले कोड को विश्लेषण, क्रमबद्ध या ऑप्टिमाइज़ करने की आवश्यकता होती है तो आपको अभिव्यक्ति की आवश्यकता होती है। अभिव्यक्ति कोड के बारे में सोचने के लिए है, Func / Action इसे चलाने के लिए है।

मैं लैम्ब्डा और Func और Action प्रतिनिधियों को समझता हूं। लेकिन अभिव्यक्ति मुझे स्टंप। आप किन स्थितियों में एक सादे पुराने Func<T> बजाय Expression<Func<T>> उपयोग करेंगे?


LINQ कैननिकल उदाहरण है (उदाहरण के लिए, डेटाबेस से बात करना), लेकिन सच में, जब भी आप वास्तव में ऐसा करने के बजाय, क्या करना है, इसे व्यक्त करने के बारे में अधिक परवाह करते हैं। उदाहरण के लिए, मैं protobuf-net (कोड-जनरेशन इत्यादि से बचने के लिए) के आरपीसी स्टैक में इस दृष्टिकोण का उपयोग करता हूं - ताकि आप इसके साथ एक विधि को कॉल कर सकें:

string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));

यह कुछ SomeMethod (और प्रत्येक तर्क का मूल्य) को हल करने के लिए अभिव्यक्ति वृक्ष को deconstructs, आरपीसी कॉल करता है, किसी भी ref / out तर्क अद्यतन करता है, और रिमोट कॉल से परिणाम देता है। अभिव्यक्ति पेड़ के माध्यम से यह केवल संभव है। मैं इसे और here कवर करता हूं।

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


जब आप अपने फ़ंक्शन को डेटा के रूप में नहीं मानते हैं और कोड के रूप में नहीं चाहते हैं तो आप एक अभिव्यक्ति का उपयोग करेंगे। यदि आप कोड (डेटा के रूप में) में हेरफेर करना चाहते हैं तो आप यह कर सकते हैं। अधिकांश समय यदि आपको अभिव्यक्ति की आवश्यकता नहीं दिखाई देती है तो आपको शायद एक का उपयोग करने की आवश्यकता नहीं है।


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

संकल्पनात्मक रूप से, Expression<Func<T>> से बिल्कुल अलग है। Func<T> एक delegate को दर्शाता है जो एक विधि और Expression<Func<T>> लिए बहुत अधिक सूचक है Expression<Func<T>> लैम्ब्डा अभिव्यक्ति के लिए एक पेड़ डेटा संरचना को दर्शाता है। यह पेड़ संरचना बताती है कि वास्तविक चीज़ करने के बजाए लैम्ब्डा अभिव्यक्ति क्या करती है। यह मूल रूप से अभिव्यक्तियों, चर, विधि कॉल, की संरचना के बारे में डेटा रखता है ... (उदाहरण के लिए इसमें यह लैम्ब्डा कुछ जानकारी है जो कुछ स्थिर + कुछ पैरामीटर है)। आप इस वर्णन का उपयोग इसे वास्तविक विधि ( Expression.Compile साथ) में परिवर्तित करने के लिए कर सकते हैं या इसके साथ अन्य सामान (जैसे LINQ से SQL उदाहरण) कर सकते हैं। लैम्बडास को अनाम विधियों और अभिव्यक्ति पेड़ के रूप में इलाज करने का कार्य पूरी तरह से संकलित समय की बात है।

Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }

प्रभावी ढंग से एक आईएल विधि को संकलित करेगा जो कुछ भी नहीं प्राप्त करता है और 10 देता है।

Expression<Func<int>> myExpression = () => 10;

एक डेटा संरचना में परिवर्तित हो जाएगा जो एक अभिव्यक्ति का वर्णन करता है जो कोई पैरामीटर नहीं प्राप्त करता है और मान 10 देता है:

बड़ी छवि

जबकि वे दोनों संकलन समय पर समान दिखते हैं, संकलक उत्पन्न करता है जो बिल्कुल अलग है


मुझे अभी तक कोई जवाब नहीं दिख रहा है जो प्रदर्शन का उल्लेख करता है। Where() या Count() में Func<> s गुजर रहा है खराब है। असली बुरा यदि आप एक Func<> उपयोग करते हैं तो यह IQueryable बजाय IEnumerable LINQ सामान को कॉल करता है, जिसका अर्थ है कि पूरी तालिकाएं खींची जाती हैं और फिर फ़िल्टर की जाती हैं। Expression<Func<>> काफी तेज़ है, खासकर यदि आप ऐसे डेटाबेस से पूछ रहे हैं जो एक और सर्वर रहता है।


मैं Func<T> और Expression<Func<T>> बीच अंतर के बारे में कुछ नोट्स जोड़ना चाहता हूं:

  • Func<T> बस एक सामान्य पुराना स्कूल मल्टीकास्ट डिलीगेट है;
  • Expression<Func<T>> अभिव्यक्ति वृक्ष के रूप में लैम्ब्डा अभिव्यक्ति का प्रतिनिधित्व है;
  • अभिव्यक्ति वृक्ष लैम्ब्डा अभिव्यक्ति वाक्यविन्यास या एपीआई वाक्यविन्यास के माध्यम से बनाया जा सकता है;
  • अभिव्यक्ति पेड़ को एक प्रतिनिधि Func<T> संकलित किया जा सकता है;
  • उलटा रूपांतरण सैद्धांतिक रूप से संभव है, लेकिन यह एक तरह का अपघटन है, इसके लिए कोई अंतर्निहित कार्यक्षमता नहीं है क्योंकि यह एक सीधी प्रक्रिया नहीं है;
  • अभिव्यक्ति वृक्ष अभिव्यक्तिविज्ञानी के माध्यम से देखा / अनुवाद / संशोधित किया जा सकता है;
  • IENumerable के लिए एक्सटेंशन विधियां Func<T> साथ संचालित होती हैं;
  • IQueryable के लिए एक्सटेंशन विधियां Expression<Func<T>> साथ संचालित होती हैं।

एक लेख है जो कोड नमूने के साथ विवरण का वर्णन करता है:
LINQ: Func <टी> बनाम अभिव्यक्ति <Func <टी >>

उम्मीद है कि यह सहायक होगा।





expression-trees