سؤال

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

للبساطة (هذه ليست حالة العمل الدقيقة)، دعنا نقول تلك الخصائص كانت CurrentIndex و RunningTotal.

يمكنني إدارة هذه الخصائص داخل حلقة foreach يدويا، لكنني أفضل تغليف هذه الوظيفة لإعادة استخدامها، ويبدو أن العداد هو المكان الصحيح.

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

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

المحلول

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

دون معرفة الكثير عن متطلبات عملك، قد تكون قادرا على الاستفادة من وظيفة مكرر، مثل شيء مثل هذا:

    public static IEnumerable<decimal> IgnoreSmallValues(List<decimal> list)
    {
        decimal runningTotal = 0M;
        foreach (decimal value in list)
        {
            // if the value is less than 1% of the running total, then ignore it
            if (runningTotal == 0M || value >= 0.01M * runningTotal)
            {
                runningTotal += value;
                yield return value;
            }
        }
    }

ثم يمكنك القيام بذلك:

        List<decimal> payments = new List<decimal>() {
            123.45M,
            234.56M,
            .01M,
            345.67M,
            1.23M,
            456.78M
        };

        foreach (decimal largePayment in IgnoreSmallValues(payments))
        {
            // handle the large payments so that I can divert all the small payments to my own bank account.  Mwahaha!
        }

محدث:

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

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

    public class FishingHook
    {
        public int Index { get; set; }
        public decimal RunningTotal { get; set; }
        public Func<decimal, bool> Criteria { get; set; }
    }

    public static IEnumerable<decimal> FishingHookIteration(IEnumerable<decimal> list, FishingHook hook)
    {
        hook.Index = 0;
        hook.RunningTotal = 0;
        foreach(decimal value in list)
        {
            // the hook object may define a Criteria delegate that
            // determines whether to skip the current value
            if (hook.Criteria == null || hook.Criteria(value))
            {
                hook.RunningTotal += value;
                yield return value;
                hook.Index++;
            }
        }
    }

سوف تستخدمها مثل هذا:

        List<decimal> payments = new List<decimal>() {
            123.45M,
            .01M,
            345.67M,
            234.56M,
            1.23M,
            456.78M
        };

        FishingHook hook = new FishingHook();

        decimal min = 0;
        hook.Criteria = x => x > min; // exclude any values that are less than/equal to the defined minimum
        foreach (decimal value in FishingHookIteration(payments, hook))
        {
            // update the minimum
            if (value > min) min = value;

            Console.WriteLine("Index: {0}, Value: {1}, Running Total: {2}", hook.Index, value, hook.RunningTotal);
        }
        // Resultint output is:
        //Index: 0, Value: 123.45, Running Total: 123.45
        //Index: 1, Value: 345.67, Running Total: 469.12
        //Index: 2, Value: 456.78, Running Total: 925.90
        // we've skipped the values .01, 234.56, and 1.23

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

نصائح أخرى

مع foreach حقا لا يمكنك الحصول على العداد - ومع ذلك، يمكنك إرجاع العداد (yield) tuple ذلك يشمل تلك البيانات؛ في الواقع، ربما يمكنك استخدام LinQ للقيام بذلك من أجلك ...

(لم أستطع ذلك نظيفة احصل على المؤشر باستخدام LinQ - يمكن الحصول على القيمة الإجمالية والحالية عبر Aggregate, ، على أية حال؛ إذن إليك نهج Tuple)

using System.Collections;
using System.Collections.Generic;
using System;
class MyTuple
{
    public int Value {get;private set;}
    public int Index { get; private set; }
    public int RunningTotal { get; private set; }
    public MyTuple(int value, int index, int runningTotal)
    {
        Value = value; Index = index; RunningTotal = runningTotal;
    }
    static IEnumerable<MyTuple> SomeMethod(IEnumerable<int> data)
    {
        int index = 0, total = 0;
        foreach (int value in data)
        {
            yield return new MyTuple(value, index++,
                total = total + value);
        }
    }
    static void Main()
    {
        int[] data = { 1, 2, 3 };
        foreach (var tuple in SomeMethod(data))
        {
            Console.WriteLine("{0}: {1} ; {2}", tuple.Index,
                tuple.Value, tuple.RunningTotal);
        }
    }
}

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

  1. تسلسل "القيمة"
  2. تسلسل "فهرس"
  3. "تشغيل إجمالي" التسلسل

ستكون الخطوة التالية هي تحديد كل من هذه التسلسلات بشكل منفصل:

List<decimal> ValueList
var Indexes = Enumerable.Range(0, ValueList.Count)

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

decimal Sum = 0;
var RunningTotals = ValueList.Select(v => Sum = Sum + v);

الخطوة الأخيرة ستكون على دفع هذه كلها معا. .NET 4 سيكون المشغل الرمز البريدي بنيت في, ، في هذه الحالة سوف تبدو مثل هذا:

var ZippedSequence = ValueList.Zip(Indexes, (value, index) => new {value, index}).Zip(RunningTotals, (temp, total) => new {temp.value, temp.index, total});

من الواضح أن هذا يحصل على مزيد من الأشياء التي تحاول دفعها معا.

في الرابط الأخير، هناك مصدر لتنفيذ وظيفة ZIP بنفسك. انها حقا قليلا قليلا من الكود.

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