سؤال

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

ربط النص

ولكن، عند تعداد شيء من هذا القبيل مجموعة عناصر التحكم من نموذج، أو مجموعة عقد من تريفيف، لقد كنت غير قادر على استخدام هذه الأنواع من التقنيات ليبدو أنها تتطلب حجة (لطريقة التمديد) الذي هو جمع IEnumerable: يمر في SomeForm.Controls لا تجميع

وكان الشيء الأكثر فائدة وجدت هذا:

نص الارتباط

والتي لا تعطيك طريقة التمديد لControl.ControlCollection مع نتيجة IEnumerable يمكنك بعد ذلك استخدام مع ينق.

ولقد تعديل المثال أعلاه لتحليل العقد من تريفيف مع أي مشكلة.

public static IEnumerable<TreeNode> GetNodesRecursively(this TreeNodeCollection nodeCollection)
{
    foreach (TreeNode theNode in nodeCollection)
    {
        yield return theNode;

        if (theNode.Nodes.Count > 0)
        {
            foreach (TreeNode subNode in theNode.Nodes.GetNodesRecursively())
            {
                yield return subNode;
            }
        }
    }
}

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

    var theNodes = treeView1.Nodes.GetNodesRecursively();

    var filteredNodes = 
    (
        from n in theNodes
            where n.Text.Contains("1")
                select n
    ).ToList();

وأعتقد أنه قد يكون هناك طريقة أكثر أناقة للقيام بذلك حيث يتم تمرير القيد (ق) في.

وماذا أريد أن أعرف ما إذا كان من الممكن تحديد هذه الإجراءات بشكل عام، لذلك ما يلي: في وقت التشغيل I يمكن أن تمر في نوع من جمع، وكذلك التحصيل الفعلي، إلى معلمة عامة، وبالتالي فإن متاحة بغض النظر عن ما اذا كان TreeNodeCollection أو Controls.Collection.

وسيكون أيضا مصلحة لي أن أعرف إذا كان هناك أي وسيلة أخرى (أرخص؟ fastser؟) من هو موضح في الرابط الثاني (أعلاه) للحصول على TreeNodeCollection أو Control.ControlCollection في شكل قابل للاستعمال من قبل ينق.

وتعليق من قبل Leppie عن 'SelectMany في SO آخر مرتبط أولا (أعلاه) يبدو وكأنه فكرة.

وقد

وتجاربي مع SelectMany: حسنا، نسميها "الكوارث". :)

ونقدر أي مؤشرات. لقد قضيت عدة ساعات قراءة كل وظيفة SO يمكن أن تجد لي أن تطرقت إلى هذه المناطق، والمشي على الأقدام في طريقي إلى هذه الغرابة باسم "ذ-combinator". A "متواضعة" تجربة، وأود أن أضيف:)

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

المحلول

وهذا الرمز يجب أن تفعل خدعة

public static class Extensions
{
    public static IEnumerable<T> GetRecursively<T>(this IEnumerable collection,
        Func<T, IEnumerable> selector)
    {
        foreach (var item in collection.OfType<T>())
        {
            yield return item;

            IEnumerable<T> children = selector(item).GetRecursively(selector);
            foreach (var child in children)
            {
                yield return child;
            }
        }
    }
}

وفيما يلي مثال على كيفية استخدامه

TreeView view = new TreeView();

// ...

IEnumerable<TreeNode> nodes = view.Nodes.
    .GetRecursively<TreeNode>(item => item.Nodes);

<القوي> تحديث:. في استجابة إلى آخر اريك ليبرت و

وهنا الكثير نسخة محسنة باستخدام تقنية مناقشتها في <لأ href = "http://blogs.msdn.com/wesdyer/archive/2007/03/23/all-about-iterators.aspx" يختلط = "نوفولو noreferrer "> كل شيء عن المكررات .

public static class Extensions
{
    public static IEnumerable<T> GetItems<T>(this IEnumerable collection,
        Func<T, IEnumerable> selector)
    {
        Stack<IEnumerable<T>> stack = new Stack<IEnumerable<T>>();
        stack.Push(collection.OfType<T>());

        while (stack.Count > 0)
        {
            IEnumerable<T> items = stack.Pop();
            foreach (var item in items)
            {
                yield return item;

                IEnumerable<T> children = selector(item).OfType<T>();
                stack.Push(children);
            }
        }
    }
}

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

نصائح أخرى

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

ودعونا نفترض الشجرة في مسألة لديها ما مجموعه العقد ن مع عمق الشجرة أقصى د <= ن.

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

وثانيا، ما تفعلونه هنا هو بناء مكرر التي تدعو مكرر التي تدعو مكرر ... بحيث ان كل MOVENEXT () على مكرر كبار في الواقع لا سلسلة من المكالمات التي هي مرة أخرى O (د) في كلفة. إذا قمت بذلك على كل عقدة، ثم كانت التكلفة الإجمالية في المكالمات هي O (الثاني) الذي هو أسوأ O حالة (ن ^ 2) وأفضل حالة O (ن إل جي ن). يمكنك أن تفعل أفضل من على حد سواء؛ ليس هناك سبب لماذا هذا لا يمكن خطية في الوقت المناسب.

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

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

الشبكي: //blogs.msdn. microsoft.com/wesdyer/2007/03/23/all-about-iterators/

وقال انه يعطي بعض تقنيات جيدة في نهاية لكتابة المكررات متكررة.

وأنا لست متأكدا من TreeNodes، ولكن هل يمكن أن تجعل من مجموعة عناصر التحكم من شكل IEnumerable باستخدام System.Linq و، على سبيل المثال

var ts = (from t in this.Controls.OfType<TextBox>
                 where t.Name.Contains("fish")
                 select t);
//Will get all the textboxes whose Names contain "fish"

وآسف أن أقول أنا لا أعرف كيف لجعل هذا العودية، من على قمة رأسي.

واستنادا إلى حل mrydengren ل:

public static IEnumerable<T> GetRecursively<T>(this IEnumerable collection,
    Func<T, IEnumerable> selector,
    Func<T, bool> predicate)
{
    foreach (var item in collection.OfType<T>())
    {
        if(!predicate(item)) continue;

        yield return item;

        IEnumerable<T> children = selector(item).GetRecursively(selector, predicate);
        foreach (var child in children)
        {
            yield return child;
        }
    }
}


var theNodes = treeView1.Nodes.GetRecursively<TreeNode>(
    x => x.Nodes,
    n => n.Text.Contains("1")).ToList();

وتحرير: لBillW

وأعتقد أنك تسأل عن شيء من هذا القبيل.

public static IEnumerable<T> <T,TCollection> GetNodesRecursively(this TCollection nodeCollection, Func<T, TCollection> getSub)
 where T, TCollection: IEnumerable
{   
    foreach (var theNode in )
    {
        yield return theNode;
        foreach (var subNode in GetNodesRecursively(theNode, getSub))
        {
            yield return subNode;
        }
    }
}

var all_control = GetNodesRecursively(control, c=>c.Controls).ToList();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top