سؤال

انا لدي Enumerable<T> وأنا أبحث عن طريقة تسمح لي بتنفيذ إجراء لكل عنصر، نوعًا ما Select ولكن بعد ذلك للآثار الجانبية.شيء مثل:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

أو

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

لقد حاولت Select(s=> { Console.WriteLine(s); return s; }), ، لكنه لم يكن يطبع أي شيء.

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

المحلول

وهناك طريقة سريعة وسهلة للحصول على ذلك هي:

Names.ToList().ForEach(e => ...);

نصائح أخرى

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

public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
  foreach (T item in value)
  {
    action(item);
  }
}

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

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


أول شيء يجب فهمه هنا هو عمليات C# linq Select(), All(), Where(), الخ، لها جذورها البرمجة الوظيفية.كانت الفكرة هي جلب بعض الأجزاء الأكثر فائدة وسهولة في البرمجة الوظيفية إلى عالم .Net.وهذا أمر مهم، لأن أحد المبادئ الأساسية للبرمجة الوظيفية هو أن تكون العمليات خالية من الآثار الجانبية.من الصعب التقليل من هذا.ومع ذلك، في حالة ForEach()/each(), ، الآثار الجانبية هي مجمل العملية.إضافة each() أو ForEach() ليس فقط خارج نطاق البرمجة الوظيفية لمشغلي linq الآخرين، ولكن في معارضة مباشرة لهم.

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

بإمكان إريك ليبرت، الذي كان عضوًا في فريق تصميم C# في ذلك الوقت، مساعدتنا هنا. ويوصي باستخدام التقليدية foreach حلقة:

يضيف [ForEach()] قوة تمثيلية جديدة إلى اللغة.يتيح لك القيام بذلك إعادة كتابة هذا الكود الواضح تمامًا:

foreach(Foo foo in foos){ statement involving foo; }

في هذا الكود:

foos.ForEach(foo=>{ statement involving foo; });

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

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate)
 {
     // now do something
 } 

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

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

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

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

يبدو هذا الرمز أكثر طبيعية.فإنه يضع foreach الكلمة الأساسية بجوار بقية الحلقة، وبعد تعريف الاستعلام.والأهم من ذلك كله، أن اسم المتغير يمكن أن يضيف معلومات جديدة ستكون مفيدة للمبرمجين المستقبليين الذين يحاولون فهم الغرض من استعلام LINQ.مرة أخرى نرى المطلوب ForEach() لم يضيف المشغل حقًا أي قوة تعبيرية جديدة إلى اللغة.

ومع ذلك، مازلنا نفتقد سمتين للافتراضية ForEach() طريقة التمديد:

  1. انها ليست قابلة للتأليف.لا أستطيع إضافة المزيد .Where() أو GroupBy() أو OrderBy() بعد foreach حلقة مضمنة مع بقية التعليمات البرمجية، دون إنشاء عبارة جديدة.
  2. انها ليست كسول.هذه العمليات تحدث على الفور.لا يسمح لي، على سبيل المثال، بالحصول على نموذج يختار فيه المستخدم عملية كحقل واحد في شاشة أكبر لا يتم التصرف عليها حتى يضغط المستخدم على زر الأمر.قد يسمح هذا النموذج للمستخدم بتغيير رأيه قبل تنفيذ الأمر.وهذا أمر طبيعي تماما (سهل حتى) مع استعلام LINQ، ولكن ليس بهذه البساطة مع أ foreach.

يمكنك بالطبع أن تصنعها بنفسك ForEach() طريقة التمديد.العديد من الإجابات الأخرى لديها تطبيقات لهذه الطريقة بالفعل؛الأمر ليس بهذا التعقيد.ومع ذلك، أشعر أنه غير ضروري.توجد بالفعل طريقة تناسب ما نريد القيام به من الناحيتين الدلالية والتشغيلية.يمكن معالجة كل من الميزات المفقودة أعلاه باستخدام الميزات الموجودة Select() المشغل أو العامل.

Select() يناسب نوع التحول أو الإسقاط الموصوف في كلا المثالين أعلاه.ومع ذلك، ضع في اعتبارك أنني سأظل أتجنب خلق آثار جانبية.الدعوة الى Select() يجب أن تعيد إما كائنات جديدة أو إسقاطات من النسخ الأصلية.يمكن المساعدة في ذلك أحيانًا من خلال استخدام نوع مجهول أو كائن ديناميكي (إذا لزم الأمر فقط).إذا كنت تريد أن تستمر النتائج في متغير القائمة الأصلي، على سبيل المثال، فيمكنك الاتصال دائمًا .ToList() وقم بإعادته إلى المتغير الأصلي.سأضيف هنا أنني أفضل العمل مع IEnumerable<T> المتغيرات قدر الإمكان على أنواع أكثر ملموسة.

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

في ملخص:

  1. مجرد التمسك بها foreach معظم الوقت.
  2. متى foreach حقًا لن تفعل ذلك (والذي ربما لا يكون كثيرًا كما تعتقد)، استخدم Select()
  3. عندما تحتاج إلى استخدام Select(), ، لا يزال بإمكانك عمومًا تجنب الآثار الجانبية (المرئية بالبرنامج)، ربما عن طريق الإسقاط على نوع مجهول.

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

http://blogs.msdn.com/kirillosenkov/ أرشيف / 2009/01/31 / foreach.aspx

من السهل إلى حد ما إضافة واحد بالرغم من ذلك.

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}

وأنت لا تستطيع أن تفعل هذا على الفور مع LINQ وIEnumerable - تحتاج إما لتنفيذ طريقة تمديد الخاص بك، أو يلقي تعداد لصفيف مع LINQ ومن ثم استدعاء Array.ForEach ():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

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

لأنه تم تصميم LINQ أن يكون ميزة الاستعلام وليس ميزة التحديث أنك لن تجد امتدادا الذي ينفذ الأساليب على IEnumerable لأن ذلك من شأنه أن يسمح لك لتنفيذ طريقة (مع احتمال الآثار الجانبية). في هذه الحالة يمكنك كذلك مجرد عصا مع

<اقتباس فقرة>   

وforeach (اسم سلسلة في أسماء)
  Console.WriteLine (اسم)؛

حسنا، يمكنك أيضا استخدام لكم الكلمة foreach القياسية، فقط تهيئته إلى oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

وعذرا، ويعتقد هذا الخيار يستحق أن يكون هنا:)

وهناك طريقة ForEach الخروج من قائمة. هل يمكن تحويل Enumerable إلى القائمة بواسطة استدعاء الأسلوب .ToList ()، ومن ثم استدعاء الأسلوب ForEach الخروج من ذلك.

وبدلا من ذلك، لقد سمعت من الناس تحديد من طريقة ForEach الخاصة الخروج من IEnumerable. ويمكن تحقيق ذلك من خلال الدعوة أساسا طريقة ForEach، ولكن بدلا من ذلك يلف هو في طريقة التمديد:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}

وعن طريق الموازي ينق:

وNames.AsParallel (). فورال (اسم => ...)

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