سؤال

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

التالية قطعتين من التعليمات البرمجية ، وهو المفضل و لماذا ؟

النسخة 1: باستخدام العائد العودة

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

الإصدار 2: عودة قائمة

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}
هل كانت مفيدة؟

المحلول

أنا أميل إلى استخدام العائد على حساب العنصر التالي في القائمة (أو حتى المجموعة التالية من العناصر).

باستخدام الإصدار 2, يجب أن يكون لديك قائمة كاملة قبل أن تعود.باستخدام العائد-عودة, كنت حقا بحاجة فقط إلى البند التالي قبل أن يعود.

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

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

في المثال لديك قائمة كاملة من المنتجات, لذلك أود أن استخدام الإصدار 2.

نصائح أخرى

ملء مؤقت قائمة مثل تحميل الفيديو كامل, في حين باستخدام yield مثل تدفق الفيديو.

كما المفاهيمي مثلا فهم عندما يجب عليك استخدام yield, دعنا نقول طريقة ConsumeLoop() عمليات البنود عاد/أسفرت عن طريق ProduceList():

void ConsumeLoop() {
    foreach (Consumable item in ProduceList())        // might have to wait here
        item.Consume();
}

IEnumerable<Consumable> ProduceList() {
    while (KeepProducing())
        yield return ProduceExpensiveConsumable();    // expensive
}

دون yield, الدعوة إلى ProduceList() قد يستغرق وقتا طويلا لأن لديك لإكمال قائمة قبل أن تعود:

//pseudo-assembly
Produce consumable[0]                   // expensive operation, e.g. disk I/O
Produce consumable[1]                   // waiting...
Produce consumable[2]                   // waiting...
Produce consumable[3]                   // completed the consumable list
Consume consumable[0]                   // start consuming
Consume consumable[1]
Consume consumable[2]
Consume consumable[3]

باستخدام yield, يصبح ترتيبها نوع من العمل "في موازاة":

//pseudo-assembly
Produce consumable[0]
Consume consumable[0]                   // immediately Consume
Produce consumable[1]
Consume consumable[1]                   // consume next
Produce consumable[2]
Consume consumable[2]                   // consume next
Produce consumable[3]
Consume consumable[3]                   // consume next

وأخيرا ، والعديد من قبل بالفعل اقترح عليك استخدام الإصدار 2 لأن لديك بالفعل الانتهاء قائمة على أي حال.

هذا هو الذهاب الى تبدو غريبة اقتراح ، لكني تعلمت كيفية استخدام yield الكلمة الرئيسية في C# من خلال القراءة عرضا عن المولدات في بايثون:ديفيد م.بيزلي هو http://www.dabeaz.com/generators/Generators.pdf.أنت لا تحتاج إلى معرفة الكثير الثعبان لفهم العرض - لم.لقد وجدت أنه من المفيد جدا في شرح ليس فقط كيف تعمل مولدات ولكن لماذا يجب أن الرعاية.

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

ملاحظة:لا تفكر في الكلمة العائد كونها مجرد طريقة أخرى لبناء المجموعة.جزء كبير من قوة العائد يأتي في حقيقة أن التنفيذ هو مؤقتا في الأسلوب أو الخاصية حتى رمز الدعوة تتكرر على القيمة التالية.هنا هو بلدي على سبيل المثال:

استخدام الكلمة العائد (إلى جانب روب Eisenburg هو Caliburn.مايكرو coroutines تنفيذ) يسمح لي أن أعبر عن استدعاء غير متزامن إلى خدمة ويب مثل هذا:

public IEnumerable<IResult> HandleButtonClick() {
    yield return Show.Busy();

    var loginCall = new LoginResult(wsClient, Username, Password);
    yield return loginCall;
    this.IsLoggedIn = loginCall.Success;

    yield return Show.NotBusy();
}

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

هنا كيف يعمل هذا:IResult لديه أسلوب التنفيذ و الانتهاء من الحدث.Caliburn.الصغير يمسك IEnumerator من الدعوة إلى HandleButtonClick() ويمررها إلى Coroutine.BeginExecute الأسلوب.على BeginExecute الطريقة يبدأ بالتكرار خلال IResults.عند أول IResult هو عاد التنفيذ مؤقتا داخل HandleButtonClick () ، BeginExecute() تعلق معالج الحدث إلى الانتهاء من الحدث المكالمات تنفيذ().IResult.تنفيذ() يمكن أن تؤدي إما متزامن أو غير متزامن المهمة حرائق الانتهاء من الحدث عندما يكون القيام به.

LoginResult تبدو شيئا مثل هذا:

public LoginResult : IResult {
    // Constructor to set private members...

    public void Execute(ActionExecutionContext context) {
        wsClient.LoginCompleted += (sender, e) => {
            this.Success = e.Result;
            Completed(this, new ResultCompletionEventArgs());
        };
        wsClient.Login(username, password);
    }

    public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
    public bool Success { get; private set; }
}

فإنه قد يساعد على إعداد شيء مثل هذه الخطوة من خلال تنفيذ لمشاهدة ما يجري.

آمل أن يساعد هذا شخص!لقد استمتعت حقا استكشاف الطرق المختلفة العائد يمكن استخدامها.

العائد العودة يمكن أن تكون قوية جدا على خوارزميات حيث تحتاج إلى تكرار خلال الملايين من الكائنات.النظر في المثال التالي حيث كنت في حاجة لحساب ممكن رحلات rideshare.أولا نحن توليد ممكن الرحلات:

    static IEnumerable<Trip> CreatePossibleTrips()
    {
        for (int i = 0; i < 1000000; i++)
        {
            yield return new Trip
            {
                Id = i.ToString(),
                Driver = new Driver { Id = i.ToString() }
            };
        }
    }

ثم أعاد من خلال كل رحلة:

    static void Main(string[] args)
    {
        foreach (var trip in CreatePossibleTrips(trips))
        {
            // possible trip is actually calculated only at this point, because of yield
            if (IsTripGood(trip))
            {
                // match good trip
            }
        }
    }

إذا كنت تستخدم قائمة بدلا من العائد, سوف تحتاج إلى تخصيص 1 مليون الكائنات الذاكرة (~190mb) و هذا مثال بسيط سوف تأخذ ~1400ms إلى تشغيل.ومع ذلك ، إذا كنت تستخدم العائد, أنت لا تحتاج إلى وضع جميع هذه temp الكائنات في الذاكرة سوف تحصل على أسرع بكثير خوارزمية السرعة:هذا المثال سوف تأخذ فقط ~400ms إلى تشغيل بدون استهلاك الذاكرة في كل شيء.

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

ليس هناك إجابة صحيحة أو خاطئة إلى هذا واحد.الذي هو واحد من الأفضل فقط يعتمد على الوضع.على سبيل المثال, إذا كان هناك الحد من الوقت الذي لديك لإكمال الاستعلام الخاص بك و عليك أن تفعل شيئا شبه معقدة مع نتائج النسخة الثانية قد يكون من الأفضل.ولكن حذار كبيرة resultsets ، خاصة إذا كنت تقوم بتشغيل هذه التعليمة البرمجية في وضع 32-بت.لقد تم عضك من قبل OutOfMemory استثناءات عدة مرات عند القيام بهذه الطريقة.

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

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

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

فإنه يساعد على القيام جليل التكرار.( الجري)

وفيما يلي فيديو بسيط التي خلقت مع كامل مظاهرة من أجل دعم نقطتين أعلاه

http://www.youtube.com/watch?v=4fju3xcm21M

هذا هو ما كريس تبيع يحكي عن تلك التصريحات في C# لغة البرمجة;

كنت في بعض الأحيان ننسى أن العائد العودة ليس هو نفسه كما تعود ، هذا الكود بعد العائد العائد يمكن تنفيذها.على سبيل المثال ، رمز بعد أول عودة هنا لا يمكن تنفيذها:

    int F() {
return 1;
return 2; // Can never be executed
}

في المقابل ، فإن رمز بعد أول العائد عودة هنا يمكن تنفيذها:

IEnumerable<int> F() {
yield return 1;
yield return 2; // Can be executed
}

هذا غالبا ما عضني في إذا كان البيان:

IEnumerable<int> F() {
if(...) { yield return 1; } // I mean this to be the only
// thing returned
yield return 2; // Oops!
}

في هذه الحالات, تذكر أن العائد العودة ليست "نهائية" مثل عودة مفيد.

على افتراض المنتجات الخاصة بك LINQ الصف يستخدم مماثلة العائد تعداد/بالتكرار ، النسخة الأولى أكثر كفاءة لأن لها سوى الرضوخ قيمة واحدة في كل مرة يتحرك أكثر.

المثال الثاني هو تحويل العداد/مكرر إلى قائمة مع ToList الأسلوب ().هذا يعني أنه يدويا تتكرر على جميع العناصر الموجودة في العداد ومن ثم إرجاع قائمة مسطحة.

هذا هو كيندا إلى جانب هذه النقطة ، ولكن لأن السؤال هو معلم أفضل الممارسات سأمضي في رمي بلدي اثنين سنتا.هذا النوع من الشيء كثيرا وأنا أفضل أن يجعل في عقار:

public static IEnumerable<Product> AllProducts
{
    get {
        using (AdventureWorksEntities db = new AdventureWorksEntities()) {
            var products = from product in db.Product
                           select product;

            return products;
        }
    }
}

بالتأكيد, انها قليلا أكثر المرجل لوحة ، لكن التعليمات البرمجية التي تستخدم هذا سوف تبدو أنظف بكثير:

prices = Whatever.AllProducts.Select (product => product.price);

مقابل

prices = Whatever.GetAllProducts().Select (product => product.price);

ملاحظة: أنا لا أفعل هذا من أجل أي من الطرق التي قد يستغرق بعض الوقت للقيام بعملهم.

و ماذا عن هذا ؟

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList();
    }
}

أعتقد أن هذا هو أنظف بكثير.ليس لدي VS2008 في متناول اليد للتحقق ، على الرغم من.في أي حال ، إذا كانت المنتجات تنفذ IEnumerable (كما يبدو - هو استعملت في بيان foreach), أود العودة مباشرة.

كنت تستخدم الإصدار 2 من القانون في هذه الحالة.منذ لديك كامل قائمة من المنتجات المتاحة و هذا ما كان متوقعا من قبل "المستهلك" من هذا الأسلوب في الدعوة ، سيكون هناك حاجة لإرسال معلومات كاملة إلى المتصل.

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

بعض الأمثلة حيث يمكن للمرء أن استخدام العائد المقابل هو:

  1. مجمع خطوة بخطوة حساب المتصل حيث ينتظر بيانات خطوة في كل مرة
  2. ترحيل الصفحات في واجهة المستخدم الرسومية حيث المستخدم قد لا تصل إلى الصفحة الأخيرة فقط مجموعة فرعية من المعلومات المطلوب الإفصاح عنها على الصفحة الحالية

للإجابة على الأسئلة الخاصة بك, كنت قد استخدمت الإصدار 2.

عودة القائمة مباشرة.الفوائد:

  • انها أكثر وضوحا
  • القائمة قابلة لإعادة الاستخدام.(مكرر لا) ليس صحيحا في الواقع ، وذلك بفضل جون

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

العائد العودة keyphrase يستخدم للحفاظ على آلة الدولة ولا جمع.أينما CLR يرى العائد العودة keyphrase المستخدمة ، CLR تنفذ العداد نمط قطعة من التعليمات البرمجية.هذا النوع من تنفيذ يساعد المطور من كل نوع من السباكة التي كنا على خلاف ذلك في غياب الكلمة.

لنفترض إذا كان المطور هو تصفية بعض جمع بالتكرار على الرغم من جمع ثم استخراج تلك الكائنات في مجموعة جديدة.هذا النوع من السباكة رتيبة جدا.

المزيد عن الكلمة هنا في هذه المقالة.

استخدام العائد يشبه الكلمة عودة, إلا أنه سيعود مولدمولد الكائن الوحيد لاجتياز مرة واحدة.

العائد وقد اثنين من المزايا:

  1. أنت لا تحتاج إلى قراءة هذه القيم مرتين ؛
  2. يمكنك الحصول على العديد من العقد الأطفال ولكن لم يكن لديك لوضع كل منهم في الذاكرة.

هناك آخر واضح تفسير ربما تساعدك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top