मैं लंबोदर Func
और Action
प्रतिनिधियों को समझता हूं । लेकिन भाव मुझे स्टंप करते हैं।
आप किन परिस्थितियों में Expression<Func<T>>
एक सादे पुराने की बजाय उपयोग करेंगे Func<T>
?
मैं लंबोदर Func
और Action
प्रतिनिधियों को समझता हूं । लेकिन भाव मुझे स्टंप करते हैं।
आप किन परिस्थितियों में Expression<Func<T>>
एक सादे पुराने की बजाय उपयोग करेंगे Func<T>
?
जवाबों:
जब आप लैम्ब्डा एक्सप्रेशन को एक्सप्रेशन ट्री के रूप में मानना चाहते हैं और उन्हें क्रियान्वित करने के बजाय उनके अंदर देखते हैं। उदाहरण के लिए, LINQ to SQL अभिव्यक्ति प्राप्त करता है और इसे समतुल्य SQL कथन में परिवर्तित करता है और इसे सर्वर (लैम्बडा को निष्पादित करने के बजाय) में जमा करता है।
वैचारिक रूप से, Expression<Func<T>>
है पूरी तरह से अलग से Func<T>
। Func<T>
निरूपित करता है delegate
जो एक विधि के लिए बहुत ज्यादा सूचक है और एक लंबोदर अभिव्यक्ति के लिए एक पेड़ डेटा संरचनाExpression<Func<T>>
को दर्शाता है । इस पेड़ की संरचना का वर्णन है कि एक लंबोदर अभिव्यक्ति वास्तविक चीज़ करने के बजाय क्या करता है। यह मूल रूप से अभिव्यक्ति, चर, विधि कॉल की संरचना के बारे में डेटा रखता है, ... (उदाहरण के लिए यह जानकारी रखता है जैसे कि यह लंबा कुछ स्थिर + कुछ पैरामीटर है)। आप इस विवरण का उपयोग इसे वास्तविक विधि में बदलने के लिए कर सकते हैं (इसके साथ ) या इसके साथ अन्य सामान (जैसे LINQ to SQL उदाहरण) कर सकते हैं। लंबोदर को गुमनाम तरीके और अभिव्यक्ति के पेड़ के रूप में मानने का कार्य विशुद्ध रूप से एक संकलन का समय है।Expression.Compile
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
प्रभावी रूप से एक IL पद्धति का संकलन करेगा जिसमें कुछ भी नहीं मिलता है और 10 रिटर्न करता है।
Expression<Func<int>> myExpression = () => 10;
एक डेटा संरचना में परिवर्तित हो जाएगा, जो एक अभिव्यक्ति का वर्णन करता है जिसे कोई पैरामीटर नहीं मिलता है और 10 मान लौटाता है:
जबकि वे दोनों संकलन समय पर समान दिखते हैं, जो संकलक उत्पन्न करता है वह पूरी तरह से अलग है ।
Expression
एक निश्चित प्रतिनिधि के बारे में मेटा-जानकारी शामिल है।
Expression<Func<...>>
केवल के बजाय उपयोग करते हैं Func<...>
।
(isAnExample) => { if(isAnExample) ok(); else expandAnswer(); }
ऐसी अभिव्यक्ति एक अभिव्यक्ति है, अगर-बयान के लिए शाखाएं बनाई जाती हैं।
मैं एक जवाब के लिए noobs जोड़ रहा हूँ क्योंकि ये जवाब मेरे सिर पर लग रहा था, जब तक मुझे एहसास नहीं हुआ कि यह कितना सरल है। कभी-कभी यह आपकी अपेक्षा है कि यह जटिल है जो आपको 'इसके चारों ओर अपना सिर लपेटने' में असमर्थ बनाता है।
मुझे तब तक अंतर को समझने की ज़रूरत नहीं थी जब तक कि मैं वास्तव में कष्टप्रद 'बग' में लिंकन-टू-एसक्यूएल का उपयोग करने की कोशिश नहीं करता।
public IEnumerable<T> Get(Func<T, bool> conditionLambda){
using(var db = new DbContext()){
return db.Set<T>.Where(conditionLambda);
}
}
यह बड़ा काम किया जब तक मुझे बड़े डेटासेट पर OutofMemoryException मिलना शुरू नहीं हुआ। लैम्ब्डा के अंदर ब्रेकपॉइंट्स सेट करने से मुझे एहसास हुआ कि यह मेरी तालिका में प्रत्येक पंक्ति के माध्यम से एक-एक करके मेरी लैम्ब्डा स्थिति के लिए मैचों की तलाश कर रहा था। इसने मुझे थोड़ी देर के लिए रोक दिया, क्योंकि बिल्ली क्यों यह मेरे डेटा टेबल को एक विशाल IEnumerable के रूप में समझ रही है कि वह लिनक्यू-टू-एसक्यूएल करने के बजाय ऐसा कर रहा है जैसे यह माना जाता है? यह मेरे LINQ-to-MongoDb समकक्ष में भी ठीक यही काम कर रहा था।
फिक्स को बस चालू Func<T, bool>
करना था Expression<Func<T, bool>>
, इसलिए मैंने गुगली की कि यहां समाप्त होने के Expression
बजाय इसकी आवश्यकता क्यों Func
है।
एक अभिव्यक्ति बस एक प्रतिनिधि को अपने बारे में एक डेटा में बदल देती है। तो a => a + 1
कुछ ऐसा हो जाता है "बाईं ओर एक है int a
। दाईं ओर आप इसमें 1 जोड़ते हैं।" बस। अब आप घर जा सकते हैं। यह स्पष्ट रूप से उससे अधिक संरचित है, लेकिन यह अनिवार्य रूप से एक अभिव्यक्ति वृक्ष वास्तव में है - अपने सिर को चारों ओर लपेटने के लिए कुछ भी नहीं।
यह समझते हुए, यह स्पष्ट हो जाता है कि LINQ-to-SQL को एक की आवश्यकता क्यों है Expression
, और Func
यह पर्याप्त नहीं है। Func
इसे अपने आप में ले जाने का एक तरीका नहीं है, यह देखने के लिए कि इसे SQL / MongoDb / अन्य क्वेरी में कैसे अनुवाद किया जा सकता है। आप देख नहीं सकते कि यह जोड़ या गुणा या घटाव है। बस आप इसे चला सकते हैं। Expression
दूसरी ओर, आपको प्रतिनिधि के अंदर देखने और वह सब कुछ देखने की अनुमति देता है जो वह करना चाहता है। यह आपको एक SQL क्वेरी की तरह, जो भी आप चाहते हैं, प्रतिनिधि को अनुवाद करने का अधिकार देता है। Func
काम नहीं किया क्योंकि मेरा DbContext लैम्ब्डा अभिव्यक्ति की सामग्री के लिए अंधा था। इस वजह से, यह लैम्ब्डा एक्सप्रेशन को SQL में नहीं बदल सका; हालांकि, इसने अगली सबसे अच्छी बात की और मेरी तालिका में प्रत्येक पंक्ति के माध्यम से उस सशर्त को पुन: प्रसारित किया।
संपादित करें: जॉन पीटर के अनुरोध पर मेरे आखिरी वाक्य पर खुलासा:
IQueryable IEnumerable का विस्तार करता है, इसलिए IEnumerable के तरीकों Where()
को स्वीकार करने वाले अधिभार प्राप्त होते हैं Expression
। जब आप उसे पास Expression
करते हैं, तो आप परिणामस्वरूप एक IQueryable रखते हैं, लेकिन जब आप पास होते हैं Func
, तो आप वापस IEnumerable आधार पर गिर रहे हैं और आपको परिणामस्वरूप IEnumerable मिलेगा। दूसरे शब्दों में, ध्यान दिए बिना आपने अपने डेटासेट को एक सूची में बदल दिया है, जिसे क्वेरी के लिए किसी चीज़ के विपरीत माना जा सकता है। जब तक आप वास्तव में हस्ताक्षर पर हुड के नीचे नहीं देखते तब तक अंतर नोटिस करना मुश्किल है।
एक्सप्रेशन बनाम फंक की पसंद में एक अत्यंत महत्वपूर्ण विचार यह है कि LINQ जैसे Entities के लिए IQueryable प्रोवाइडर जो आप एक्सप्रेशन में पास करते हैं, उसे pass पचा ’सकते हैं, लेकिन फनक में आप जो पास करते हैं, उसे अनदेखा कर देंगे। मेरे पास इस विषय पर दो ब्लॉग पोस्ट हैं:
LINQ के साथ एंटिटी फ्रेमवर्क और फॉलिंग इन लव के साथ एक्सप्रेशन बनाम फंक पर अधिक - भाग 7: एक्सप्रेशंस और फंक (अंतिम बार)
मैं के बीच मतभेद के बारे में कुछ नोट्स जोड़ना चाहते हैं Func<T>
और Expression<Func<T>>
:
Func<T>
सिर्फ एक सामान्य पुराना स्कूल है मल्टीकास्टडेलगेट;Expression<Func<T>>
अभिव्यक्ति वृक्ष के रूप में लैम्ब्डा अभिव्यक्ति का प्रतिनिधित्व है;Func<T>
;ExpressionVisitor
;Func<T>
;Expression<Func<T>>
।एक लेख है जो कोड नमूनों के साथ विवरण का वर्णन करता है:
LINQ: फंक <टी> बनाम अभिव्यक्ति <फंक <टी >> ।
आशा है कि यह मददगार होगा।
Krzysztof Cwalina की पुस्तक ( फ्रेमवर्क डिज़ाइन गाइडलाइन्स: कन्वेंशन, मुहावरे और पुन: प्रयोज्य .NET पुस्तकालयों के लिए पैटर्न ) से इसके बारे में अधिक दार्शनिक व्याख्या है ;
गैर-छवि संस्करण के लिए संपादित करें:
अधिकांश बार आप फंक या एक्शन चाहते हैं, अगर ऐसा करने की आवश्यकता है तो कुछ कोड चलाने के लिए है। कोड के विश्लेषण, क्रमबद्ध या चलने से पहले अनुकूलित किए जाने पर आपको अभिव्यक्ति की आवश्यकता होती है। अभिव्यक्ति कोड के बारे में सोचने के लिए है, फंक / एक्शन इसे चलाने के लिए है।
database.data.Where(i => i.Id > 0)
रूप में निष्पादित किया जाना चाहिए SELECT FROM [data] WHERE [id] > 0
। तुम सिर्फ एक समारोह में पारित, तो आप अपने ड्राइवर पर blinders डाल दिया है और सभी यह कर सकते हैं है SELECT *
प्रत्येक और फिल्टर के माध्यम से और फिर एक बार यह स्मृति में है कि डेटा के सभी भरी हुई है, पुनरावृति आईडी> 0. अपने रैपिंग के साथ सब कुछ बाहर Func
में Expression
अधिकार ड्राइवर का विश्लेषण करने के लिए Func
और इसे Sql / MongoDb / अन्य क्वेरी में बदल दें।
Expression
लेकिन जब मैं छुट्टी पर रहूंगा तो वह होगा Func/Action
;)
LINQ कैनोनिकल उदाहरण है (उदाहरण के लिए, डेटाबेस से बात करना), लेकिन वास्तव में, किसी भी समय आप वास्तव में ऐसा करने के बजाय क्या करना है, इसे व्यक्त करने के बारे में अधिक परवाह करते हैं। उदाहरण के लिए, मैं प्रोटॉफ़-नेट के आरपीसी स्टैक में इस दृष्टिकोण का उपयोग करता हूं (कोड-पीढ़ी आदि से बचने के लिए) - इसलिए आप इसके साथ एक विधि कहते हैं:
string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
यह SomeMethod
(और प्रत्येक तर्क का मान) हल करने के लिए अभिव्यक्ति ट्री को डिक्रिप्ट करता है, आरपीसी कॉल करता है, किसी भी ref
/ अपडेट को अपडेट करता है out
, और रिमोट कॉल से परिणाम लौटाता है। यह अभिव्यक्ति पेड़ के माध्यम से ही संभव है। मैं इसे यहां और कवर करता हूं ।
एक अन्य उदाहरण यह है कि जब आप लैम्बडा संकलित करने के उद्देश्य से अभिव्यक्ति के पेड़ मैन्युअल रूप से बना रहे हैं, जैसा कि सामान्य ऑपरेटर्स कोड द्वारा किया जाता है ।
जब आप अपने फ़ंक्शन को डेटा के रूप में और कोड के रूप में व्यवहार करना चाहते हैं, तो आप एक अभिव्यक्ति का उपयोग करेंगे। यदि आप कोड (डेटा के रूप में) में हेरफेर करना चाहते हैं तो आप ऐसा कर सकते हैं। अधिकांश समय यदि आपको अभिव्यक्ति की आवश्यकता नहीं दिखती है, तो आपको संभवतः एक का उपयोग करने की आवश्यकता नहीं है।
प्राथमिक कारण यह है कि जब आप कोड को सीधे नहीं चलाना चाहते हैं, बल्कि इसका निरीक्षण करना चाहते हैं। यह किसी भी कारण से हो सकता है:
Expression
एक प्रतिनिधि के रूप में अनुक्रमित करना असंभव हो सकता है, क्योंकि किसी भी अभिव्यक्ति में एक मनमाने ढंग से प्रतिनिधि / विधि संदर्भ का आह्वान हो सकता है। "ईज़ी" रिश्तेदार है, ज़ाहिर है।
मुझे अभी तक कोई जवाब नहीं मिला है जिसमें प्रदर्शन का उल्लेख है। पासिंग Func<>
में रों Where()
या Count()
बुरा है। असली बुरा। यदि आप एक का उपयोग करते हैं, Func<>
तो इसके IEnumerable
बजाय LINQ सामान को कॉल करता है IQueryable
, जिसका अर्थ है कि पूरी तालिकाओं को अंदर खींच लिया जाता है और फिर फ़िल्टर किया जाता है। Expression<Func<>>
यदि आप किसी ऐसे डेटाबेस को क्वेरी कर रहे हैं जो किसी अन्य सर्वर पर रहता है, तो विशेष रूप से तेजी से महत्वपूर्ण है।