ما هو العائد الكلمة المستخدمة في C# ؟

StackOverflow https://stackoverflow.com/questions/39476

  •  09-06-2019
  •  | 
  •  

سؤال

في كيف يمكنني تعرض سوى جزء من IList<> السؤال واحدة من الإجابات التالية التعليمات البرمجية المتكررة:

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

ماذا الكلمة العائد تفعل هناك ؟ لقد رأيت ذلك المشار إليها في بضعة أماكن أخرى السؤال لكني لم افهم تماما ما في الواقع لا.أنا معتاد على التفكير في العائد في معنى واحد الخيط العائد إلى آخر, لكن هذا لا يبدو ذات الصلة هنا.

هل كانت مفيدة؟

المحلول

على yield كلمات فعلا الكثير جدا هنا.

تقوم الدالة بإرجاع كائن التي تطبق IEnumerable<object> واجهة.إذا كانت وظيفة الاتصال يبدأ foreachجي على هذا الكائن ، يتم استدعاء الدالة مرة أخرى حتى أنه "ينتج".هذا هو نحوي السكر أدخلت في C# 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);
                }
            }
        }
    }
}

نصائح أخرى

التكرار.فإنه يخلق آلة الدولة "تحت الأغطية" أن يتذكر أين كنت في كل دورة من وظيفة تلتقط من هناك.

العائد اثنين كبيرة الاستخدامات ،

  1. فإنه يساعد على توفير مخصص التكرار دون خلق temp مجموعات.

  2. فإنه يساعد على القيام جليل التكرار.enter image description here

من أجل توضيح نقطتين أعلاه أكثر برهاني ، لقد خلق بسيط فيديو يمكنك مشاهدته هنا

مؤخرا ريمون تشن ركض أيضا مثيرة للاهتمام سلسلة من المقالات حول الكلمة العائد.

في حين انها اسميا استخدامها بسهولة تنفيذ التكرار نمط ، ولكن يمكن تعميمها في آلة الدولة.لا فائدة نقلا عن رايموند الجزء الأخير أيضا وصلات إلى استخدامات أخرى (ولكن على سبيل المثال في Entin بلوق هو esp جيدة ، والتي تبين كيفية كتابة المتزامن آمنة رمز).

في أول وهلة ، العائد المقابل هو .صافي السكر إعادة 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();
    }
}

الاستفادة من استخدام العائد هو أنه إذا كانت وظيفة تستهلك البيانات الخاصة بك ببساطة يحتاج البند الأول من جمع بقية البنود التي لن يتم إنشاء.

العائد المشغل يسمح إنشاء البنود كما هو طالب.هذا سبب جيد لاستخدامه.

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() طريقة ثم الدولة سيتم إعادة تعيين لذلك.

حدسي ، الكلمة بإرجاع قيمة من وظيفة دون أن يترك ذلك أيفي التعليمات البرمجية المثال فإنه يعود الحالي item قيمة ثم يستأنف الحلقة.أكثر رسميا ، يتم استخدامه من قبل برنامج التحويل البرمجي لإنشاء مدونة على مكرر.التكرار هي وظائف تلك العودة IEnumerable الكائنات.على MSDN لديها العديد من المواد عنهم.

قائمة أو تنفيذ مجموعة الأحمال كافة العناصر على الفور في حين أن العائد تنفيذ يوفر تأجيل تنفيذ الحل.

في الواقع, غالبا ما يكون من المرغوب فيه أن تنفيذ الحد الأدنى من العمل حسب الحاجة من أجل الحد من استهلاك الموارد من تطبيق.

على سبيل المثال, قد يكون لديك تطبيق عملية الملايين من السجلات من قاعدة البيانات.الفوائد التالية يمكن أن يتحقق عندما نستخدم 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
مرحبا بوب

لطيفة!فقط أول اتصال تم بناؤها عند العميل "سحب" هذا البند من جمع.

هنا هو طريقة بسيطة لفهم مفهوم:الفكرة الأساسية هي, إذا كنت ترغب في جمع التي يمكنك استخدامها "foreach"،ولكن جمع العناصر في مجموعة مكلفة لسبب ما (مثل الاستعلام لهم قاعدة بيانات) ، وسوف كثير من الأحيان لا تحتاج إلى كامل المجموعة ، ثم إنشاء وظيفة أن يبني جمع عنصر واحد في وقت واحد و ينتج عن ذلك العودة إلى المستهلك (الذي يمكن أن ثم إنهاء جهد جمع في وقت مبكر).

التفكير في الأمر بهذه الطريقة: تذهب إلى اللحوم العداد ترغب في شراء رطل من شرائح لحم الخنزير.الجزار يأخذ 10 رطل من لحم الخنزير إلى الخلف ، يضعه على تقطيع, شرائح كل شيء ، ثم يجلب كومة من شرائح لك و تدابير رطل من ذلك.(الطريقة القديمة).مع yield, الجزار يجلب تقطيع العداد و يبدأ التقطيع و "الغلة" كل شريحة على نطاق واسع حتى أنه تدابير 1 جنيه ، ثم يلف ذلك من أجلك أنت. الطريقة القديمة قد يكون من الأفضل بالنسبة الجزار (يتيح له تنظيم آلية له الطريقة التي يحب) ، ولكن الطريقة الجديدة بوضوح أكثر كفاءة في معظم الحالات بالنسبة للمستهلك.

على yield الكلمات الرئيسية يسمح لك لخلق IEnumerable<T> في شكل على مكرر كتلة.هذا التكرار كتلة يدعم تأجيل تنفيذ و إذا كنت لم تكن مألوفة مع مفهوم قد تظهر سحرية.ومع ذلك ، في نهاية اليوم هو رمز فقط أن ينفذ دون أي غريب الحيل.

مكرر كتلة يمكن وصفها بأنها نحوية السكر حيث ينشئ برنامج التحويل البرمجي آلة الدولة أن يتتبع مدى تعداد enumerable وقد تقدم.تعداد غير قابلة للتعداد ، غالبا ما تستخدم 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 البيانات معشق مع التعليمات البرمجية الأخرى.

تعداد مكرر كتلة foreach حلقة المستخدمة:

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

هنا هو الإخراج (لا مفاجآت هنا):

Begin
1
After 1
2
After 2
42
End

كما ذكر أعلاه foreach هو نحوي السكر:

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

في محاولة فك هذا أنا مفطور تسلسل الرسم البياني مع التجريدات إزالة:

C# iterator block sequence diagram

آلة الدولة التي تم إنشاؤها بواسطة برنامج التحويل البرمجي كما تنفذ العداد ولكن لجعل الرسم البياني أكثر وضوحا لقد أظهرت لهم حالات منفصلة.(عندما تكون الدولة آلة المذكورة من موضوع آخر كنت لا تحصل في الواقع حالات منفصلة ولكن التفاصيل ليست مهمة هنا.)

في كل مرة كنت اتصل مكرر كتلة مثيل جديد من آلة الدولة يتم إنشاؤه.ومع ذلك ، فإن أيا من التعليمات البرمجية الخاصة بك في التكرار يتم تنفيذ كتلة حتى enumerator.MoveNext() ينفذ للمرة الأولى.هذا هو كيف تأجيل تنفيذ الأعمال.هنا هو (سخيف) على سبيل المثال:

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

عند هذه النقطة مكرر لم تنفذ.على Where شرط يخلق جديد IEnumerable<T> أن يلتف IEnumerable<T> عاد قبل IteratorBlock ولكن هذا enumerable لم يتم سردها.يحدث هذا عندما تقوم بتنفيذ foreach حلقة:

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

إذا كنت تعداد enumerable مرتين ثم مثيل جديد من آلة الدولة هو خلق كل الوقت و التكرار كتلة سيتم تنفيذ نفس القانون مرتين.

لاحظ أن ينق أساليب مثل ToList(), ToArray(), First(), Count() الخ.سوف تستخدم foreach حلقة تعداد enumerable.على سبيل المثال ToList() تعداد كافة عناصر enumerable وتخزينها في قائمة.يمكنك الآن الوصول إلى قائمة للحصول على جميع العناصر enumerable دون مكرر كتلة المنفذة مرة أخرى.هناك مفاضلة بين استخدام وحدة المعالجة المركزية لإنتاج عناصر enumerable عدة مرات و الذاكرة لتخزين عناصر التعداد للوصول إليها عدة مرات عند استخدام أساليب مثل ToList().

إذا فهمت هذا بشكل صحيح, هنا كيف أن هذه العبارة من منظور وظيفة تنفيذ IEnumerable مع العائد.

  • هنا واحد.
  • ندعو مرة أخرى إذا كنت في حاجة أخرى.
  • سوف أتذكر ما أعطيتك.
  • أنا فقط أعرف ما إذا كنت يمكن أن تعطيك آخر عند الاتصال مرة أخرى.

C# العائد الكلمة ، بكل بساطة ، ويسمح العديد من المكالمات إلى الجسم من التعليمات البرمجية ، يشار إلى التكرار أن يعرف كيفية العودة قبل أن يتم ذلك ، عندما دعا مرة أخرى ، تستمر من حيث توقفت - أيفإنه يساعد على التكرار تصبح بشفافية جليل في كل بند في تسلسل التي مكرر عوائد المكالمات المتتابعة.

في جافا سكريبت ، نفس المفهوم يسمى المولدات.

إنها طريقة بسيطة جدا وسهلة لإنشاء enumerable على وجوه الخاص بك.المحول البرمجي بإنشاء فئة يلف الطريقة التي تطبق في هذه الحالة ، IEnumerable<object>.دون الكلمة العائد سيكون لديك لإنشاء كائن التي تطبق IEnumerable<object>.

انها تنتج enumerable تسلسل.ماذا يفعل هو في الواقع خلق المحلية IEnumerable تسلسل وإعادته كوسيلة النتيجة

هذا الرابط وقد مثال بسيط

حتى أبسط الأمثلة هنا

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

لاحظ أن العائد يعود لن يعود من طريقة.يمكنك حتى وضع WriteLine بعد yield return

سبق ينتج IEnumerable من 4 رجات 4,4,4,4

هنا WriteLine.سيتم إضافة 4 إلى القائمة ، حروف الطباعة ، ثم إضافة 4 إلى القائمة ، ثم أكمل طريقة حتى تعود حقا من طريقة(مرة واحدة طريقة أنجزت ، كما سيحدث مع الإجراء دون عودة).ولكن هذا من شأنه أن يكون لها قيمة ، IEnumerable قائمة ints, أنه يعود على الانتهاء.

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

انها محاولة لجلب بعض روبي الخير :)
المفهوم: هذه بعض نماذج روبي البرمجية التي يطبع بها كل عنصر من المصفوفة

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

مجموعة كل طريقة التنفيذ ينتج السيطرة على المتصل (إن 'يضع x') مع كل عنصر من المصفوفة بدقة عرض×.يمكن المتصل ثم افعل كل ما يحتاج إلى القيام به مع x.

ومع ذلك .صافي لا يذهب كل في طريقه إلى هنا..C# يبدو أن يكون إلى جانب العائد مع IEnumerable بطريقة يجبرك على كتابة حلقة foreach في المتصل كما رأينا في Mendelt رد.أقل أنيقة.

//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]; 
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top