LINQ और IEnumerable<T>
पुल आधारित है। इसका अर्थ यह है कि सामान्य रूप से LINQ कथन का हिस्सा होने वाले भविष्यवाणियों और कार्रवाइयों को तब तक निष्पादित नहीं किया जाता है जब तक मूल्यों को खींचा नहीं जाता है। इसके अलावा भविष्यवाणी और क्रियाएं हर बार मूल्यों को खींचा जाएगा (उदाहरण के लिए कोई गुप्त कैशिंग चल रहा है)।
एक IEnumerable<T>
से खींच foreach
बयान है जो वास्तव में IEnumerable<T>.GetEnumerator()
बुला और बार-बार मूल्यों को खींचने के लिए IEnumerator<T>.MoveNext()
को फोन करके एक प्रगणक प्राप्त करने के लिए वाक्यात्मक चीनी है द्वारा किया जाता है।
ToList()
,
ToArray()
,
ToDictionary()
और
ToLookup()
तरह
LINQ ऑपरेटरों एक foreach
बयान लपेटता तो इन तरीकों एक पुल करेंगे। Aggregate()
, Count()
और First()
जैसे ऑपरेटरों के बारे में भी यही कहा जा सकता है। इन विधियों में आम बात है कि वे एक एकल परिणाम उत्पन्न करते हैं जिसे foreach
कथन निष्पादित करके बनाया जाना है।
कई LINQ ऑपरेटर एक नया IEnumerable<T>
अनुक्रम उत्पन्न करते हैं। जब परिणामी अनुक्रम से एक तत्व खींचा जाता है तो ऑपरेटर स्रोत अनुक्रम से एक या अधिक तत्व खींचता है। Select()
ऑपरेटर सबसे स्पष्ट उदाहरण है लेकिन अन्य उदाहरण SelectMany()
, Where()
, Concat()
, Union()
, Distinct()
, Skip()
और Take()
हैं। ये ऑपरेटर कोई कैशिंग नहीं करते हैं। जब 0'से N'th तत्व खींचा जाता है तो यह स्रोत अनुक्रम से N'th तत्व खींचता है, आपूर्ति की गई क्रिया का उपयोग करके प्रक्षेपण लागू करता है और इसे वापस करता है। यहाँ कुछ भी गुप्त नहीं जा रहा है।
अन्य LINQ ऑपरेटरों ने भी IEnumerable<T>
अनुक्रमों का उत्पादन किया है, लेकिन वे वास्तव में पूरे स्रोत अनुक्रम को खींचकर, अपना काम कर रहे हैं और फिर एक नया अनुक्रम तैयार करके कार्यान्वित किए जाते हैं। इन विधियों में Reverse()
, OrderBy()
और GroupBy()
शामिल हैं। हालांकि, ऑपरेटर द्वारा किए गए पुल को केवल तभी किया जाता है जब ऑपरेटर को खींच लिया जाता है जिसका मतलब है कि आपको अभी भी कुछ भी निष्पादित होने से पहले LINQ कथन के "अंत में" foreach
लूप की आवश्यकता है। आप तर्क दे सकते हैं कि ये ऑपरेटर कैश का उपयोग करते हैं क्योंकि वे तुरंत संपूर्ण स्रोत अनुक्रम खींचते हैं। हालांकि, यह कैश प्रत्येक बार ऑपरेटर को फिर से चालू किया जाता है, इसलिए यह वास्तव में एक कार्यान्वयन विवरण है और कुछ ऐसा नहीं है जो जादुई रूप से पता लगाएगा कि आप उसी अनुक्रम में एक ही OrderBy()
ऑपरेशन को कई बार लागू कर रहे हैं।
आपके उदाहरण में ToList()
एक पुल करेगा। बाहरी Select
में कार्रवाई 100 बार निष्पादित होगी। प्रत्येक बार जब यह क्रिया Aggregate()
निष्पादित की जाती है तो एक और पुल करेगा जो एक्सएमएल विशेषताओं को पार्स करेगा। कुल मिलाकर आपका कोड Int32.Parse()
200 बार कॉल करेगा।
आप विशेषताओं खींच कर एक बार के बजाय प्रत्येक यात्रा पर इस सुधार कर सकते हैं:
var X = XElement.Parse (@"
<ROOT>
<MUL v='2' />
<MUL v='3' />
</ROOT>
")
.Elements()
.Select (t => Int32.Parse (t.Attribute ("v").Value))
.ToList();
Enumerable.Range (1, 100)
.Select (s => x.Aggregate (s, (t, u) => t * u))
.ToList()
.ForEach (s => Console.WriteLine (s));
अब Int32.Parse()
केवल 2 बार कहा जाता है। हालांकि, लागत यह है कि विशेषता मूल्यों की एक सूची आवंटित, संग्रहित और अंततः कचरा एकत्रित किया जाना चाहिए। (सूची में दो तत्व होते हैं जब कोई बड़ी चिंता नहीं होती है।)
ध्यान दें कि यदि आप पहले ToList()
भूल जाते हैं जो गुण खींचता है तो कोड अभी भी चल जाएगा लेकिन मूल कोड के समान सटीक प्रदर्शन विशेषताओं के साथ। गुणों को संग्रहीत करने के लिए कोई स्थान उपयोग नहीं किया जाता है लेकिन उन्हें प्रत्येक पुनरावृत्ति पर पार्स किया जाता है।
"मैं इस तरह कुछ ऐसा करने के बारे में कैसे जाउंगा" - सबसे अच्छा शॉट इस कोड से उत्पन्न आईएल का अध्ययन करना है। – Andrey
आप पार्स() विधि पर एक डीबगर ब्रेकपॉइंट सेट कर सकते हैं और देख सकते हैं कि यह कितनी बार हिट करता है। प्रतिक्रिया के लिए –