teacher - indexer in c#




foreach مقابل someList.ForEach(){} (9)

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

النوع الأول:

List<string> someList = <some way to init>
foreach(string s in someList) {
   <process the string>
}

طريق اخر:

List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
    <process the string>
});

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


أظن someList.ForEach() يمكن بسهولة someList.ForEach() دعوة someList.ForEach() حين أن foreach العادي ليس من السهل تشغيله بالتوازي. يمكنك بسهولة تشغيل العديد من المندوبين المختلفين على النوى المختلفة ، وهو أمر ليس من السهل القيام به مع foreach عادي.
فقط سنتي 2


أعرف شيئين غامضين يجعلهما مختلفين. اذهب لي!

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

    // A list of actions to execute later
    List<Action> actions = new List<Action>();

    // Numbers 0 to 9
    List<int> numbers = Enumerable.Range(0, 10).ToList();

    // Store an action that prints each number (WRONG!)
    foreach (int number in numbers)
        actions.Add(() => Console.WriteLine(number));

    // Run the actions, we actually print 10 copies of "9"
    foreach (Action action in actions)
        action();

    // So try again
    actions.Clear();

    // Store an action that prints each number (RIGHT!)
    numbers.ForEach(number =>
        actions.Add(() => Console.WriteLine(number)));

    // Run the actions
    foreach (Action action in actions)
        action();

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

(للأسف ، أعتقد أن ForEach عضو في قائمة ، بدلاً من طريقة تمديد ، على الرغم من أنه من السهل تعريفه بنفسك بحيث يكون لديك هذا المرفق على أي نوع قابل للتعداد.)

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

المزيد هنا


تعتبر List.ForEach () أكثر وظيفية.

List.ForEach() يقول ما تريد القيام به. foreach(item in list) كما يقول بالضبط كيف تريد القيام به. هذا يترك قائمة. List.ForEach حرة لتغيير تنفيذ جزء في المستقبل. على سبيل المثال ، قد تعمل نسخة افتراضية مستقبلية من .Net دائمًا على تشغيل List.ForEach بالتوازي ، تحت افتراض أن لدى كل شخص عددًا من نوى cpu تكون عادة في وضع الخمول.

من ناحية أخرى ، يمنحك foreach (item in list) تحكمًا أكثر قليلاً في الحلقة. على سبيل المثال ، أنت تعلم أن العناصر سيتم تكراراتها في نوع معين من الترتيب المتسلسل ، ويمكنك بسهولة كسر في الوسط إذا كان أحد العناصر يفي بشرط ما.


تعتبر الدالة ForEach عضوًا في قائمة الفئات العامة.

لقد قمت بإنشاء الامتداد التالي لإعادة إنتاج الكود الداخلي:

public static class MyExtension<T>
    {
        public static void MyForEach(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection)
                action.Invoke(item);
        }
    }

حتى النهاية نستخدم foreach عادي (أو حلقة إذا كنت تريد).

من ناحية أخرى ، فإن استخدام وظيفة المفوض هو مجرد طريقة أخرى لتعريف وظيفة ، هذا الكود:

delegate(string s) {
    <process the string>
}

ما يعادل:

private static void myFunction(string s, <other variables...>)
{
   <process the string>
}

أو باستخدام تعابير labda:

(s) => <process the string>

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

http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx


كان لدينا بعض التعليمات البرمجية هنا (في VS2005 و C # 2.0) حيث ذهب المهندسون السابقون من طريقهم إلى استخدام list.ForEach( delegate(item) { foo;}); بدلاً من foreach(item in list) {foo; }; foreach(item in list) {foo; }; لجميع الرموز التي كتبوها. على سبيل المثال كتلة من التعليمات البرمجية لقراءة الصفوف من DataReader.

ما زلت لا أعرف بالضبط لماذا فعلوا هذا.

عيوب list.ForEach() هي:

  • هو أكثر مطولاً في C # 2.0. ومع ذلك ، في C # 3 فصاعدًا ، يمكنك استخدام بناء الجملة " => " لإجراء بعض التعبيرات الدقيقة.

  • إنها أقل شيوعًا. سوف يتساءل الناس الذين عليهم الحفاظ على هذا الرمز لماذا فعلت ذلك بهذه الطريقة. استغرق الأمر بعض الوقت ليقرر أنه لم يكن هناك أي سبب ، ربما باستثناء جعل الكاتب يبدو ذكياً (جودة البقية من الشفرة قوضت ذلك). كان أيضاً أقل قابلية للقراءة ، مع " }) " في نهاية كتلة التعليمات البرمجية المفوَّضة.

  • راجع أيضًا كتاب Bill Wagner "Effective C #: 50 طرقًا خاصة لتحسين C #" حيث يتحدث عن سبب تفضيل foreach إلى حلقات أخرى مثل الحلقات أو أثناءها - النقطة الأساسية هي أنك ترك المترجم يقرر أفضل طريقة لبناء الحلقة. إذا تمكن إصدار مستقبلي من المترجم من استخدام تقنية أسرع ، فستحصل على هذا مجانًا باستخدام foreach وإعادة البناء ، بدلاً من تغيير الشفرة.

  • يسمح لك إنشاء foreach(item in list) باستخدام break أو continue إذا كنت بحاجة إلى إنهاء التكرار أو الحلقة. ولكن لا يمكنك تغيير القائمة داخل حلقة foreach.

أنا مندهش لرؤية هذه list.ForEach أسرع قليلا. ولكن هذا على الأرجح ليس سبباً وجيهاً لاستخدامه في كل مكان ، وهذا سيكون تحسينًا مبكرًا. إذا كان التطبيق الخاص بك يستخدم قاعدة بيانات أو خدمة ويب ، لا يكون التحكم دائمًا هو المكان الذي ينتقل فيه الوقت. وهل قارنته مقابل حلقة أيضا؟ يمكن أن تكون list.ForEach أسرع بسبب استخدام ذلك داخليًا و يمكن أن تكون حلقة for بدون التضمين أسرع.

أنا لا أوافق على أن list.ForEach(delegate) " list.ForEach(delegate) "أكثر وظيفية" بأي طريقة هامة. إنه يمر بوظيفة إلى وظيفة ، ولكن لا يوجد فرق كبير في النتائج أو تنظيم البرنامج.

لا أعتقد أن foreach(item in list) "يقول بالضبط كيف تريد أن يتم ذلك" - a for(int 1 = 0; i < count; i++) حلقة يفعل ذلك ، حلقة foreach يترك خيار التحكم حتى المجمع.

شعوري هو ، في مشروع جديد ، لاستخدام foreach(item in list) لمعظم الحلقات من أجل الانضمام إلى الاستخدام المشترك list.Foreach() للقراءة ، واستخدام list.Foreach() فقط للكتل القصيرة ، عندما يمكنك القيام بشيء أكثر بشكل أنيق أو مضغوط مع مشغل C # 3 " => ". في مثل هذه الحالات ، قد يكون هناك بالفعل طريقة تمديد LINQ أكثر تحديدًا من ForEach() . انظر ما إذا كان Where() أو Select() أو Any() أو All() أو Max() أو أحد أساليب LINQ الأخرى لا يقوم بالفعل بما تريده من الحلقة.


هناك تمييز واحد مهم ومفيد بين الاثنين.

لأن .ForEach يستخدم حلقة for iterate المجموعة ، وهذا صحيح (تحرير: قبل .net 4.5 - تم تغيير التنفيذ ثم كلاهما):

someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); }); 

بينما يستخدم foreach ، لذلك هذا غير صالح:

foreach(var item in someList)
  if(item.RemoveMe) someList.Remove(item);

tl ؛ dr: لا copypaste هذا الرمز في التطبيق الخاص بك!

هذه الأمثلة ليست أفضل الممارسات ، فهي فقط لإثبات الاختلافات بين ForEach() و foreach .

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

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


هناك شيء واحد يجب الحذر منه هو كيفية الخروج من طريقة Generic .ForEach - راجع هذه المناقشة . على الرغم من أن الرابط يبدو أن هذه الطريقة هي الأسرع. لست متأكدا لماذا - كنت أعتقد أنها ستكون تعادل مرة واحدة تجميعها ...


يمكنك تسمية المفوض المجهول :-)

ويمكنك كتابة الثاني على النحو التالي:

someList.ForEach(s => s.ToUpper())

التي أفضلها ، وتوفر الكثير من الكتابة.

وكما يقول يواكيم ، فإن التوازي أسهل للتطبيق على النموذج الثاني.







enumeration