C # में किस कीवर्ड का उपयोग किया जाता है?


827

में कैसे कर सकते मैं बेनकाब IList <का केवल एक टुकड़ा> जवाब का सवाल एक निम्नलिखित कोड का टुकड़ा था:

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}

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


इसके बारे में सिर्फ MSDN लिंक यहाँ है msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
डेवलपर

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

4
मैंने डॉक्स पढ़ा है, लेकिन मुझे डर है कि मुझे अभी भी समझ में नहीं आया है :(
ओरटंड जूल

जवाबों:


737

yieldकीवर्ड वास्तव में काफी एक बहुत कुछ यहाँ है।

फ़ंक्शन IEnumerable<object>इंटरफ़ेस को लागू करने वाली ऑब्जेक्ट देता है। यदि कोई कॉलिंग फ़ंक्शन foreachइस ऑब्जेक्ट पर शुरू होता है , तो फ़ंक्शन को "पैदावार" होने तक फिर से बुलाया जाता है। यह सी # 2.0 में प्रस्तुत की जाने वाली चीनी है । पहले के संस्करणों में आपको इस तरह से सामान बनाने के लिए अपनी IEnumerableऔर IEnumeratorवस्तुओं को बनाना पड़ता था।

इस तरह से समझने का सबसे आसान तरीका कोड है, उदाहरण के लिए, कुछ ब्रेकपॉइंट सेट करें और देखें कि क्या होता है। इस उदाहरण के माध्यम से कदम उठाने की कोशिश करें:

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

जब आप उदाहरण के माध्यम से कदम बढ़ाते हैं, तो आपको Integers()रिटर्न के लिए पहला कॉल मिलेगा 1। दूसरा कॉल रिटर्न 2और लाइन yield return 1फिर से निष्पादित नहीं किया जाता है।

यहाँ एक वास्तविक जीवन उदाहरण है:

public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
    using (var connection = CreateConnection())
    {
        using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
        {
            command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return make(reader);
                }
            }
        }
    }
}

113
इस मामले में, यह आसान होगा, मैं सिर्फ यहाँ पूर्णांक का उपयोग करके दिखाता हूं कि उपज वापसी कैसे काम करती है। यील्ड रिटर्न का उपयोग करने के बारे में अच्छी बात यह है कि यह इटरेटर पैटर्न को लागू करने का एक बहुत तेज़ तरीका है, इसलिए चीजों का मूल्यांकन आलसी तरीके से किया जाता है।
Mendelt

110
यह भी ध्यान देने योग्य है कि आप yield break;तब उपयोग कर सकते हैं जब आप कोई और आइटम वापस नहीं करना चाहते हैं।
रोरी

7
yieldकोई कीवर्ड नहीं है। यदि ऐसा होता तो मैं उपज को पहचानकर्ता के रूप में उपयोग नहीं कर सकता थाint yield = 500;
ब्रैंडिन

5
@Brandin ऐसा इसलिए है क्योंकि सभी प्रोग्रामिंग भाषाएं आरक्षित और प्रासंगिक दो प्रकार के कीवर्ड का समर्थन करती हैं। उपज बाद की श्रेणी में आती है, यही वजह है कि आपका कोड C # संकलक द्वारा निषिद्ध नहीं है। यहाँ अधिक विवरण: ericlippert.com/2009/05/11/reserved-and-contextual-keywords आपको यह जानकर रोमांचित होगा कि ऐसे आरक्षित शब्द भी हैं जिन्हें किसी भाषा के कीवर्ड के रूप में मान्यता नहीं है। उदाहरण के लिए जावा में गोटो। यहां अधिक जानकारी: stackoverflow.com/questions/2545103/…
RBT

6
'If a calling function starts foreach-ing over this object the function is called again until it "yields"'। मुझे सही नहीं लगता। मैंने हमेशा "फसल पैदावार की पैदावार" के संदर्भ में "पैदावार के लिए कार की पैदावार" के संदर्भ में सी # उपज कीवर्ड के बारे में सोचा।
जेक

369

पुनरावृत्ति। यह एक राज्य मशीन बनाता है "कवर के नीचे" जो याद करता है कि आप फ़ंक्शन के प्रत्येक अतिरिक्त चक्र पर थे और वहां से उठाते हैं।


209

यील्ड के दो बेहतरीन उपयोग हैं,

  1. यह अस्थायी संग्रह बनाने के बिना कस्टम पुनरावृत्ति प्रदान करने में मदद करता है।

  2. यह स्टेटफुल इट्रीशन करने में मदद करता है। यहां छवि विवरण दर्ज करें

दो से अधिक बिंदुओं को स्पष्ट रूप से समझाने के लिए, मैंने एक सरल वीडियो बनाया है जिसे आप यहां देख सकते हैं


13
वीडियो मुझे स्पष्ट रूप से समझने में मदद करता है yield। @ शिवप्रसादकौरला की कोड प्रोजेक्ट लेख C # Yield का उपयोग क्या है? इसी स्पष्टीकरण का एक अच्छा स्रोत यह भी है
21

मैं एक तीसरे बिंदु के रूप में भी yieldजोड़ूंगा जो एक कस्टम IEnumerator बनाने के लिए "तेज़" तरीका है (बल्कि एक वर्ग IEnumerator इंटरफ़ेस को लागू करने के लिए)।
MrTourkos

मैंने आपका वीडियो शिवप्रसाद देखा और यह स्पष्ट रूप से उपज कीवर्ड के उपयोग की खोज की।
टॉरस्टेड

वीडियो के लिए धन्यवाद!। बहुत अच्छी तरह से समझाया!
Roblogic

महान वीडियो, लेकिन आश्चर्य की बात है ... उपज का उपयोग करने वाला कार्यान्वयन स्पष्ट रूप से क्लीनर है, लेकिन यह अनिवार्य रूप से राज्य का ट्रैक रखने के लिए आंतरिक रूप से अपनी खुद की मेमोरी या / और सूची बनाना चाहिए (या राज्य मशीन बनाने के लिए)। तो, क्या "यील्ड" कार्यान्वयन को सरल बनाने और चीजों को बेहतर बनाने के अलावा कुछ और कर रहा है या इसमें कुछ और है? दक्षता के बारे में, बिना अधिक से अधिक या कम कुशल / तेज के यील्ड का उपयोग करके कोड कैसे चल रहा है?
कठिन संयोग

135

हाल ही में रेमंड चेन ने उपज कीवर्ड पर लेखों की एक दिलचस्प श्रृंखला चलाई।

हालांकि यह मुख्य रूप से इट्रेटर पैटर्न को आसानी से लागू करने के लिए उपयोग किया जाता है, लेकिन इसे राज्य मशीन में सामान्यीकृत किया जा सकता है। रेमंड को उद्धृत करने का कोई मतलब नहीं है, अंतिम भाग अन्य उपयोगों के लिए भी लिंक करता है (लेकिन एंटिन के ब्लॉग में उदाहरण एस्प अच्छा है, जिसमें दिखाया गया है कि कैसे async सुरक्षित कोड लिखना है)।


इसके लिए मतदान करना होगा। स्वीट वह ऑपरेटर और इंटर्न के उद्देश्य को कैसे समझाता है।
साजिद्निज़ामी

3
भाग 1, "यील्ड रिटर्न" के वाक्यगत शर्करा की व्याख्या करता है। बहुत बढ़िया!
Dror Weiss

99

पहली नजर में, उपज वापसी एक है नेट एक वापस जाने के लिए चीनी IEnumerable

उपज के बिना, संग्रह के सभी आइटम एक ही बार में बनाए जाते हैं:

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        return new List<SomeData> {
            new SomeData(), 
            new SomeData(), 
            new SomeData()
        };
    }
}

पैदावार का उपयोग कर समान कोड, यह आइटम द्वारा आइटम लौटाता है:

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        yield return new SomeData();
        yield return new SomeData();
        yield return new SomeData();
    }
}

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

उपज संचालक वस्तुओं की मांग के अनुसार निर्माण की अनुमति देता है। वह इसका उपयोग करने का एक अच्छा कारण है।


40

yield returnप्रगणकों के साथ प्रयोग किया जाता है। उपज स्टेटमेंट के प्रत्येक कॉल पर, कॉल करने वाले को नियंत्रण वापस कर दिया जाता है, लेकिन यह सुनिश्चित करता है कि कैली की स्थिति बनी रहे। इसके कारण, जब कॉलर अगले तत्व की गणना करता है, तो यह कथन के तुरंत बाद कथन से विधि में निष्पादन जारी रखता है yield

इसे एक उदाहरण से समझने का प्रयास करते हैं। इस उदाहरण में, प्रत्येक पंक्ति के अनुरूप मैंने उस क्रम का उल्लेख किया है जिसमें निष्पादन प्रवाह होता है।

static void Main(string[] args)
{
    foreach (int fib in Fibs(6))//1, 5
    {
        Console.WriteLine(fib + " ");//4, 10
    }            
}

static IEnumerable<int> Fibs(int fibCount)
{
    for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
    {
        yield return prevFib;//3, 9
        int newFib = prevFib + currFib;//6
        prevFib = currFib;//7
        currFib = newFib;//8
    }
}

इसके अलावा, राज्य प्रत्येक गणना के लिए बनाए रखा जाता है। मान लीजिए, मेरे पास एक और कॉल करने की Fibs()विधि है तो राज्य इसके लिए रीसेट हो जाएगा।


2
सेट प्रीफ़िब = 1 - पहला फाइबोनैचि संख्या एक "1" है, "0" नहीं
फ़ुबो

31

सहज रूप से, कीवर्ड फ़ंक्शन को छोड़ने के बिना फ़ंक्शन से एक मान लौटाता है, अर्थात आपके कोड उदाहरण में यह वर्तमान itemमान लौटाता है और फिर लूप को फिर से शुरू करता है। औपचारिक रूप से, इसका उपयोग कंपाइलर द्वारा एक इटरेटर के लिए कोड जनरेट करने के लिए किया जाता है । Iterators फ़ंक्शंस हैं जो IEnumerableऑब्जेक्ट लौटाते हैं। MSDN कई है लेख उनके बारे में।


4
खैर, सटीक होने के लिए यह लूप को फिर से शुरू नहीं करता है, यह तब तक रोक देता है जब तक कि माता-पिता "iterator.next ()" को कॉल नहीं करते।
एलेक्स

8
@ जिटबिट यही कारण है कि मैंने "सहज रूप से" और "अधिक औपचारिक रूप से" का उपयोग किया।
कोनराड रूडोल्फ

31

एक सूची या सरणी कार्यान्वयन सभी वस्तुओं को तुरंत लोड करता है जबकि उपज कार्यान्वयन एक स्थगित निष्पादन समाधान प्रदान करता है।

व्यवहार में, किसी अनुप्रयोग के संसाधन की खपत को कम करने के लिए आवश्यकतानुसार कम से कम काम करना अक्सर वांछनीय होता है।

उदाहरण के लिए, हमारे पास एक एप्लिकेशन हो सकता है जो डेटाबेस से लाखों रिकॉर्ड की प्रक्रिया करता है। निम्न लाभ तब प्राप्त किए जा सकते हैं जब हम IEnumerable को आस्थगित निष्पादन पुल-आधारित मॉडल में उपयोग करते हैं:

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

यहाँ एक संग्रह के निर्माण के बीच पहली तुलना की गई है जैसे कि उपज का उपयोग करने की तुलना में एक सूची।

सूची उदाहरण

    public class ContactListStore : IStore<ContactModel>
    {
        public IEnumerable<ContactModel> GetEnumerator()
        {
            var contacts = new List<ContactModel>();
            Console.WriteLine("ContactListStore: Creating contact 1");
            contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
            Console.WriteLine("ContactListStore: Creating contact 2");
            contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
            Console.WriteLine("ContactListStore: Creating contact 3");
            contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
            return contacts;
        }
    }

    static void Main(string[] args)
    {
        var store = new ContactListStore();
        var contacts = store.GetEnumerator();

        Console.WriteLine("Ready to iterate through the collection.");
        Console.ReadLine();
    }

कंसोल आउटपुट
ContactListStore: संपर्क बनाना 1
ContactListStore: संपर्क 2
बनाना ContactListStore: संपर्क 3
संग्रह के माध्यम से पुनरावृति करने के लिए तैयार।

नोट: सूची में एक भी आइटम के बिना पूरे संग्रह को मेमोरी में लोड किया गया था

उपज उदाहरण

public class ContactYieldStore : IStore<ContactModel>
{
    public IEnumerable<ContactModel> GetEnumerator()
    {
        Console.WriteLine("ContactYieldStore: Creating contact 1");
        yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
        Console.WriteLine("ContactYieldStore: Creating contact 2");
        yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
        Console.WriteLine("ContactYieldStore: Creating contact 3");
        yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
    }
}

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();

    Console.WriteLine("Ready to iterate through the collection.");
    Console.ReadLine();
}

कंसोल आउटपुट
संग्रह के माध्यम से पुनरावृति के लिए तैयार है।

नोट: संग्रह को बिल्कुल भी निष्पादित नहीं किया गया था। यह IEnumerable की "स्थगित निष्पादन" प्रकृति के कारण है। किसी वस्तु का निर्माण तभी होगा जब उसकी वास्तव में आवश्यकता होगी।

चलो संग्रह को फिर से बुलाते हैं और व्यवहार को रोकते हैं जब हम संग्रह में पहला संपर्क प्राप्त करते हैं।

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();
    Console.WriteLine("Ready to iterate through the collection");
    Console.WriteLine("Hello {0}", contacts.First().FirstName);
    Console.ReadLine();
}

कंसोल आउटपुट
संग्रह के माध्यम से पुनरावृति के लिए तैयार है
ContactYieldStore: संपर्क 1
हैलो बॉबबनाना

अच्छा! केवल पहले संपर्क का निर्माण तब किया गया जब ग्राहक ने संग्रह से आइटम को "खींच" लिया।


1
इस उत्तर पर अधिक ध्यान देने की आवश्यकता है! Thx
leon22

@ leon22 बिल्कुल +2
SNR

26

यहां अवधारणा को समझने का एक सरल तरीका है: मूल विचार है, यदि आप एक संग्रह चाहते हैं जिसका उपयोग आप " foreach" पर कर सकते हैं , लेकिन संग्रह में वस्तुओं को इकट्ठा करना किसी कारण से महंगा है (जैसे उन्हें डेटाबेस से बाहर क्वेरी करना) और आपको अक्सर पूरे संग्रह की आवश्यकता नहीं होगी, फिर आप एक फ़ंक्शन बनाते हैं जो एक बार में एक आइटम का संग्रह बनाता है और इसे वापस उपभोक्ता को देता है (जो तब संग्रह प्रयास को जल्दी समाप्त कर सकता है)।

इसे इस तरह से सोचें: आप मांस काउंटर पर जाते हैं और कटा हुआ हैम का एक पाउंड खरीदना चाहते हैं। कसाई एक 10-पाउंड हैम को पीछे ले जाता है, इसे स्लाइसर मशीन पर रखता है, पूरी चीज को स्लाइस करता है, फिर स्लाइस के ढेर को वापस आपके पास लाता है और इसका एक पाउंड निकालता है। (पुराना तरीका)। yieldकसाई के साथ काउंटर पर स्लाइसर मशीन लाता है, और 1-पाउंड के माप तक स्केल पर प्रत्येक स्लाइस को "पैदावार" और "उपज" देना शुरू कर देता है, फिर इसे आपके लिए लपेटता है और आपका काम हो जाता है। कसाई के लिए ओल्ड वे बेहतर हो सकता है (उसे अपनी मशीनरी को अपनी पसंद के अनुसार व्यवस्थित करने देता है), लेकिन नया तरीका स्पष्ट रूप से उपभोक्ता के लिए ज्यादातर मामलों में अधिक कुशल है।


18

yieldकीवर्ड आप एक बनाने की अनुमति देता IEnumerable<T>एक पर फार्म में iterator ब्लॉक । यह इटरेटर ब्लॉक आवर्ती निष्पादन का समर्थन करता है और यदि आप अवधारणा से परिचित नहीं हैं तो यह लगभग जादुई दिखाई दे सकता है। हालांकि, दिन के अंत में यह केवल कोड है जो बिना किसी अजीब चाल के निष्पादित होता है।

एक इट्रिक्टर ब्लॉक को सिंटैक्टिक शुगर के रूप में वर्णित किया जा सकता है जहां कंपाइलर एक राज्य मशीन बनाता है जो ट्रैक करता है कि एन्यूमरेबल की गणना कितनी दूर तक आगे बढ़ी है। किसी एन्यूमरेट को एन्यूमरेट करने के लिए, आप अक्सर एक foreachलूप का उपयोग करते हैं । हालांकि, एक foreachलूप भी सिंथेटिक चीनी है। तो आप वास्तविक कोड से हटाए गए दो अमूर्त हैं, यही वजह है कि शुरू में यह समझना मुश्किल हो सकता है कि यह सब एक साथ कैसे काम करता है।

मान लें कि आपके पास एक बहुत ही सरल पुनरावृत्ति अवरोधक है:

IEnumerable<int> IteratorBlock()
{
    Console.WriteLine("Begin");
    yield return 1;
    Console.WriteLine("After 1");
    yield return 2;
    Console.WriteLine("After 2");
    yield return 42;
    Console.WriteLine("End");
}

रियल इटरेटर ब्लॉक में अक्सर स्थितियां और लूप होते हैं, लेकिन जब आप शर्तों की जांच करते हैं और लूप को अनियंत्रित करते हैं तो वे अभी भी समाप्त हो जाते हैं क्योंकि yieldबयान अन्य कोड के साथ interleaved हैं।

पुनरावृत्ति करने के लिए पुनरावृत्ति अवरोधक a foreach लूप का उपयोग किया जाता है:

foreach (var i in IteratorBlock())
    Console.WriteLine(i);

यहाँ आउटपुट है (यहाँ कोई आश्चर्य नहीं):

शुरू
1
1 के बाद
2
2 के बाद
42
समाप्त

जैसा कि ऊपर कहा गया foreachहै, शक्करयुक्त चीनी:

IEnumerator<int> enumerator = null;
try
{
    enumerator = IteratorBlock().GetEnumerator();
    while (enumerator.MoveNext())
    {
        var i = enumerator.Current;
        Console.WriteLine(i);
    }
}
finally
{
    enumerator?.Dispose();
}

इसे हटाने के प्रयास में मैंने एक अनुक्रम आरेख को हटा दिया है जिसमें हटाए गए सार हैं:

सी # इटरेटर ब्लॉक अनुक्रम आरेख

संकलक द्वारा बनाई गई राज्य मशीन भी एन्यूमरेटर को लागू करती है लेकिन आरेख को अधिक स्पष्ट करने के लिए मैंने उन्हें अलग-अलग उदाहरणों के रूप में दिखाया है। (जब राज्य मशीन दूसरे धागे से एनुमरेट की जाती है तो आपको वास्तव में अलग-अलग उदाहरण मिलते हैं, लेकिन यह विवरण यहां महत्वपूर्ण नहीं है)

हर बार जब आप अपने itter ब्लॉक को कॉल करते हैं तो स्टेट मशीन का एक नया उदाहरण बनता है। हालाँकि, इटरेटर ब्लॉक में आपका कोई भी कोड enumerator.MoveNext()पहली बार निष्पादित होने तक निष्पादित नहीं होता है। इस प्रकार कार्य को स्थगित कर दिया जाता है। यहाँ एक (बल्कि मूर्खतापूर्ण) उदाहरण है:

var evenNumbers = IteratorBlock().Where(i => i%2 == 0);

इस बिंदु पर पुनरावृत्ति निष्पादित नहीं किया गया है। Whereखंड एक नया बनाता है IEnumerable<T>कि wraps IEnumerable<T>द्वारा दिया IteratorBlockलेकिन इस गणनीय अभी तक प्रगणित किया जाना है। यह तब होता है जब आप एक foreachलूप निष्पादित करते हैं :

foreach (var evenNumber in evenNumbers)
    Console.WriteLine(eventNumber);

यदि आप गणना करने योग्य दो बार गणना करते हैं तो हर बार राज्य मशीन का एक नया उदाहरण बनाया जाता है और आपका पुनरावृत्ति खंड एक ही कोड को दो बार निष्पादित करेगा।

सूचना है कि LINQ तरीकों की तरह ToList(), ToArray(), First(), Count()आदि एक का उपयोग करेगा foreachपाश गणनीय की गणना करने में। उदाहरण के लिए ToList(), गणना करने वाले सभी तत्वों की गणना की जाएगी और उन्हें एक सूची में संग्रहीत किया जाएगा। अब आप पुनरावृत्‍ति ब्‍लॉक किए हुए पुनरावृत्‍ति ब्‍लॉकर के सभी तत्वों को पाने के लिए सूची तक पहुँच सकते हैं। सीपीयू का उपयोग करने के बीच एक व्यापार बंद है, जिसमें कई बार उपयोग करने के लिए कई बार उपयोग करने के लिए एन्यूमरेशन के तत्वों को स्टोर करने के लिए एन्यूमरबल के तत्वों का उत्पादन किया जाता है ToList()


18

यदि मैं इसे सही ढंग से समझता हूं, तो यहां मैं इसे उपज के साथ IEnumerable को लागू करने वाले फ़ंक्शन के परिप्रेक्ष्य से कैसे वाक्यांश दूंगा।

  • यहां एक है।
  • यदि आपको दूसरे की आवश्यकता हो तो फिर से कॉल करें।
  • मुझे याद होगा कि मैंने तुम्हें पहले से क्या दिया था।
  • जब आप दोबारा कॉल करेंगे तो मुझे केवल यह पता चलेगा कि क्या मैं आपको दूसरा दे सकता हूं।

सरल और शानदार
हैरी

10

C # यील्ड कीवर्ड, इसे सीधे शब्दों में कहें, तो कोड के एक निकाय को कई कॉल करने की अनुमति देता है, जिसे एक पुनरावृत्तिकर्ता के रूप में संदर्भित किया जाता है, जो जानता है कि इसे पूरा करने से पहले कैसे लौटना है और जब दोबारा बुलाया जाता है, तो यह जारी रहता है कि यह कहाँ छोड़ा गया है - अर्थात यह एक पुनरावृत्ति में मदद करता है क्रमिक रूप से प्रत्येक आइटम को इस क्रम में स्टेटफुल हो जाता है कि पुनरावर्ती लगातार कॉल करता है।

जावास्क्रिप्ट में, समान अवधारणा को जेनरेटर कहा जाता है।


सबसे अच्छा स्पष्टीकरण अभी तक। क्या ये भी अजगर में समान जनरेटर हैं?
पेट्रोसम

7

यह एक बहुत ही सरल और आसान तरीका है, जो आपकी वस्तु के लिए एक पहेली बना सकता है। कंपाइलर एक ऐसा वर्ग बनाता है जो आपके तरीके को लागू करता है और जो लागू होता है, इस मामले में, IEnumerable <ऑब्जेक्ट>। उपज कीवर्ड के बिना, आपको एक ऑब्जेक्ट बनाना होगा जो IEnumerable <ऑब्जेक्ट> को लागू करता है।


5

यह अनगिनत अनुक्रम का उत्पादन कर रहा है। यह वास्तव में स्थानीय IEnumerable अनुक्रम बना रहा है और एक विधि परिणाम के रूप में इसे वापस कर रहा है


3

इस लिंक का एक सरल उदाहरण है

और भी सरल उदाहरण यहाँ हैं

public static IEnumerable<int> testYieldb()
{
    for(int i=0;i<3;i++) yield return 4;
}

ध्यान दें कि उपज वापसी विधि से वापस नहीं आएगी। तुम भी एक के WriteLineबाद रख सकते हैंyield return

उपरोक्त 4 ints 4,4,4,4 के IEnumerable का उत्पादन करता है

यहाँ एक के साथ WriteLine। सूची में 4 जोड़ेंगे, एबीसी प्रिंट करेंगे, फिर सूची में 4 जोड़ेंगे, फिर विधि को पूरा करेंगे और इसलिए वास्तव में विधि से वापस लौटेंगे (एक बार विधि पूरी हो गई है, जैसा कि बिना वापसी के एक प्रक्रिया के साथ होगा)। लेकिन यह एक मूल्य होगा, एस की एक IEnumerableसूची है int, कि यह पूरा होने पर लौटता है।

public static IEnumerable<int> testYieldb()
{
    yield return 4;
    console.WriteLine("abc");
    yield return 4;
}

यह भी ध्यान रखें कि जब आप उपज का उपयोग करते हैं, तो आप जो वापस लौट रहे हैं वह फ़ंक्शन के समान प्रकार का नहीं है। यह IEnumerableसूची के भीतर एक तत्व का प्रकार है ।

आप विधि का रिटर्न प्रकार के साथ उपज का उपयोग करते हैं IEnumerable। यदि विधि का रिटर्न प्रकार है intया List<int>आप उपयोग करते हैं yield, तो यह संकलन नहीं करेगा। आप IEnumerableपैदावार के बिना IEnumerableविधि वापसी प्रकार का उपयोग कर सकते हैं, लेकिन ऐसा लगता है कि आप विधि वापसी प्रकार के बिना उपज का उपयोग नहीं कर सकते ।

और इसे निष्पादित करने के लिए आपको इसे एक विशेष तरीके से कॉल करना होगा।

static void Main(string[] args)
{
    testA();
    Console.Write("try again. the above won't execute any of the function!\n");

    foreach (var x in testA()) { }


    Console.ReadLine();
}



// static List<int> testA()
static IEnumerable<int> testA()
{
    Console.WriteLine("asdfa");
    yield return 1;
    Console.WriteLine("asdf");
}

नोट- अगर SelectMany को समझने की कोशिश की जा रही है, तो यह उपज और जेनेरिक का भी उपयोग करता है .. इस उदाहरण से मदद मिल सकती है public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { yield return t; }और public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { return new List<TResult>(); }
Barlop

बहुत अच्छी व्याख्या की तरह लग रहा है! यह स्वीकृत उत्तर हो सकता था।
पोंगापुंडित

@pongapundit धन्यवाद, मेरा उत्तर निश्चित रूप से स्पष्ट और सरल है, लेकिन मैंने खुद को बहुत अधिक उपयोग नहीं किया है, अन्य उत्तरदाताओं को इसके साथ बहुत अधिक अनुभव है और इसके उपयोग की जानकारी मुझे है। यहाँ मैंने उपज के बारे में जो कुछ लिखा था, वह शायद मेरे सिर को खरोंचने से था, यहाँ कुछ उत्तरों को जानने की कोशिश कर रहा था और उस बिंदु पर था! लेकिन जब से मैं yield returnउस अच्छी तरह से (मैं उल्लेख किया सरल बात के अलावा) नहीं जानता, और यह बहुत ज्यादा इस्तेमाल नहीं किया है और इसके उपयोग को बहुत अधिक नहीं जानता है, मुझे नहीं लगता कि यह स्वीकार किया जाना चाहिए।
बार्लोप

3

यील्ड कीवर्ड के बारे में एक प्रमुख बिंदु आलसी निष्पादन है । अब मुझे आलसी निष्पादन से क्या मतलब है कि जरूरत पड़ने पर निष्पादित करें। इसे लगाने का एक बेहतर तरीका एक उदाहरण देना है

उदाहरण: यील्ड का उपयोग नहीं अर्थात आलसी निष्पादन नहीं।

        public static IEnumerable<int> CreateCollectionWithList()
        {
            var list =  new List<int>();
            list.Add(10);
            list.Add(0);
            list.Add(1);
            list.Add(2);
            list.Add(20);

            return list;
        }

उदाहरण: यील्ड यानी आलसी निष्पादन का उपयोग करना।

    public static IEnumerable<int> CreateCollectionWithYield()
    {
        yield return 10;
        for (int i = 0; i < 3; i++) 
        {
            yield return i;
        }

        yield return 20;
    }

अब जब मैं दोनों तरीकों को बुलाता हूं।

var listItems = CreateCollectionWithList();
var yieldedItems = CreateCollectionWithYield();

आप सूचियों को देखेंगे। इसके अंदर 5 आइटम होंगे (डीबगिंग करते समय सूची पर अपने माउस को घुमाएं)। जबकि यील्डइमेट्स में सिर्फ विधि का संदर्भ होगा न कि वस्तुओं का। इसका मतलब है कि इसने विधि के अंदर आइटम प्राप्त करने की प्रक्रिया को निष्पादित नहीं किया है। केवल जरूरत पड़ने पर डेटा प्राप्त करने का एक बहुत ही कुशल तरीका। उपज का वास्तविक कार्यान्वयन ORM में देखा जा सकता है जैसे कि Entity Framework और NHibernate आदि।


-3

यह कुछ रूबी अच्छाई लाने की कोशिश कर रहा है :)
अवधारणा: यह कुछ नमूना रूबी कोड है जो सरणी के प्रत्येक तत्व को प्रिंट करता है

 rubyArray = [1,2,3,4,5,6,7,8,9,10]
    rubyArray.each{|x| 
        puts x   # do whatever with x
    }

सरणी प्रत्येक विधि कार्यान्वयन है पैदावार फोन करने वाले के लिए पर नियंत्रण के साथ ( 'कहते हैं एक्स') प्रत्येक सरणी के तत्व बड़े करीने से एक्स के रूप में प्रस्तुत किया। इसके बाद कॉल करने वाले को x के साथ जो भी करना है वह कर सकता है।

तथापि .Net यहाँ सभी तरह से नहीं जाती है .. C # के पास IEnumerable के साथ युग्मित उपज है, एक तरह से आपको कॉल करने वाले को फॉरेस्ट लूप लिखने के लिए मजबूर करता है जैसा कि मेंडेल्ट की प्रतिक्रिया में देखा गया है। थोड़ा कम सुरुचिपूर्ण।

//calling code
foreach(int i in obCustomClass.Each())
{
    Console.WriteLine(i.ToString());
}

// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
   for(int iLooper=0; iLooper<data.Length; ++iLooper)
        yield return data[iLooper]; 
}

7
-1 यह जवाब मुझे सही नहीं लगता। हां, C # yieldके साथ युग्मित है IEnumerable, और C # में "ब्लॉक" की रूबी अवधारणा का अभाव है। लेकिन सी # में लैम्ब्डा है, जो एक ForEachविधि के कार्यान्वयन की अनुमति दे सकता है , बहुत समान रूबी eachइसका मतलब यह नहीं है कि ऐसा करना एक अच्छा विचार होगा , हालांकि।
15'13

अभी तक बेहतर: सार्वजनिक IEnumerable <int> प्रत्येक () {int index = 0; उपज वापसी डेटा [सूचकांक ++]; }
अता
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.