سؤال

أود أن تفعل أي ما يعادل التالية في LINQ, ولكن أنا لا يمكن معرفة كيف:

IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());

ما هو حقيقي syntax ؟

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

المحلول

وليس هناك تمديد ForEach لIEnumerable. فقط لList<T>. لذلك يمكن أن تفعل

items.ToList().ForEach(i => i.DoStuff());

وبدلا من ذلك، والكتابة الخاصة طريقة تمديد ForEach الخاص بك:

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

نصائح أخرى

وقد وفرت

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

وهذا ليس لنقول ان هذا أمر سيء للقيام - مجرد التفكير في الأسباب الفلسفية وراء هذا القرار

التحديث 7/17/2012:كما يبدو من C# 5.0, سلوك foreach هو موضح أدناه قد تغيرت "استخدام foreach التكرار متغير متداخل امدا التعبير لم تعد تنتج نتائج غير متوقعة."هذا الجواب لا ينطبق على C# ≥ 5.0.

@جون السكيت وكل شخص يفضل foreach الكلمة.

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

نأخذ الأمثلة التالية باستخدام ما يعادل وظيفيا طريقة التمديد في @فريدريك Kalseth الجواب.

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

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

الاختبار التالي باستخدام "ForEach" طريقة التمديد يمر:

[Test]
public void ForEachExtensionWin()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                values.ForEach(value => 
                                    source.OnNext(() => value));

                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Win
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}

التالية فشلت مع الخطأ:

المتوقع:أي ما يعادل < 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 > ولكن كان:< 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 >

[Test]
public void ForEachKeywordFail()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                foreach (var value in values)
                                {
                                    //If you have resharper, notice the warning
                                    source.OnNext(() => value);
                                }
                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Fail
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}

هل يمكن استخدام ملحق FirstOrDefault()، والذي يتوفر لIEnumerable<T>. من خلال العودة false من المسند، سيتم تشغيله لكل عنصر ولكن لا يهمني أنه لا يجد في الواقع مباراة. هذا وسوف تجنب الحمل ToList().

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });

وأخذت طريقة فريدريك وتعديل نوع الإرجاع.

وبهذه الطريقة، ويدعم طريقة <م> إعدام مؤجلة مثل الطرق LINQ أخرى.

تعديل: إذا كان هذا غير واضح، أي استخدام هذه الطريقة على يجب أن تنتهي مع ToList () أو أي وسيلة أخرى لإجبار طريقة للعمل على استكمال enumerable. خلاف ذلك، لن يتم تنفيذ العمل!

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach (T item in enumeration)
    {
        action(item);
        yield return item;
    }
}

وهنا اختبار للمساعدة نرى ما يلي:

[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
    IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};

    var sb = new StringBuilder();

    enumerable
        .ForEach(c => sb.Append("1"))
        .ForEach(c => sb.Append("2"))
        .ToList();

    Assert.That(sb.ToString(), Is.EqualTo("121212"));
}

إذا قمت بإزالة <قوية> ToList () في النهاية، سترى اختبار فشل منذ ب StringBuilder يحتوي على سلسلة فارغة. هذا هو لأنه لا يوجد طريقة أجبر ForEach تعداد.

حافظ على الآثار الجانبية للخروج من بلدي IEnumerable

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

وأود أن تفعل ما يعادل ما يلي في LINQ، ولكن أنا لا يمكن معرفة كيفية القيام بذلك:

  

وكما أشار آخرون إلى هنا والخارج LINQ، ومن المتوقع أن تكون الآثار الجانبية الحرة أساليب IEnumerable.

هل تريد حقا أن "لا شيء" إلى كل عنصر في IEnumerable؟ ثم foreach هو الخيار الافضل. الناس لم يفاجأ عندما تحدث الآثار الجانبية هنا.

foreach (var i in items) i.DoStuff();

أراهن أنك لا تريد من الآثار الجانبية

ولكن في تجربتي الآثار الجانبية عادة ما تكون غير مطلوبة. في كثير من الأحيان لم يكن هناك استعلام LINQ بسيطة تنتظر من يكتشفها يرافقه إجابة StackOverflow.com إما عن طريق جون السكيت، إريك ليبرت، أو مارك Gravell شرح كيفية تفعل ما تريد!

بعض الأمثلة

إذا كنت في الواقع مجرد تجميع (تراكم) بعض القيمة ثم عليك أن تنظر في طريقة تمديد Aggregate.

items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));

وربما كنت ترغب في إنشاء IEnumerable جديدة من القيم الموجودة.

items.Select(x => Transform(x));

وأو ربما كنت ترغب في إنشاء جدول نظرة المتابعة:

items.ToLookup(x, x => GetTheKey(x))

وقائمة (لعبة الكلمات لا يقصد تماما) من الاحتمالات تطول وتطول.

إذا كنت تريد أن تكون بمثابة فات التعداد يجب أن تحقق كل بند.

public static class EnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
    {
        foreach (var item in enumeration)
        {
            action(item);
            yield return item;
        }
    }
}

وهناك إطلاق تجريبي من قبل مايكروسوفت ل الامتدادات التفاعلية لLINQ (أيضا على NuGet ، انظر> وأ href = "HTTPS: // شبكة الاتصالات العالمية .nuget.org / لمحات / rxteam "يختلط =" noreferrer "> RxTeams الشخصية للحصول على المزيد من الروابط). و قناة 9 فيديو يفسر ذلك جيدا .

ويتم تقديم مستندات لها إلا في شكل XML. لقد واجه هذه الوثائق rel="noreferrer"> href="http://files.me.com/jwigger/cwysjb" لتسمح لها أن تكون في شكل أكثر قابلية للقراءة. بفك أرشيف مستندات وابحث عن index.html و .

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

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));

ووفقا لPLINQ (متوفر منذ صافي 4.0)، يمكنك القيام

IEnumerable<T>.AsParallel().ForAll() 

وللقيام حلقة foreach موازية على IEnumerable.

والغرض من ForEach هو تتسبب في آثار جانبية. IEnumerable هو لتعداد كسول مجموعة.

وهذا الاختلاف المفاهيمي مرئيا تماما عندما كنت تنظر فيه.

وSomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

وهذا لن تنفيذ حتى كنت تفعل "العد" أو "ToList ()" أو شيء من هذا على ذلك. ومن الواضح ليس ما يتم التعبير.

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

وكثير من الناس ذكر ذلك، ولكن كان علي أن تدونها. أليس هذا أكثر واضحة / أكثر قابلية للقراءة؟

IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();

وقصيرة وبسيطة (ش).

والآن لدينا خيار ...

        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
        parallelOptions.MaxDegreeOfParallelism = 1;
#endif
        Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

وبطبيعة الحال، وهذا يفتح علبة جديدة كاملة من threadworms.

ملاحظة (آسف حول الخطوط، انها ما قرر النظام)

هذا "النهج الوظيفي" التجريد التسريبات وقت كبير.لا على مستوى اللغة يمنع الآثار الجانبية.طالما أنك يمكن أن تجعل الاتصال الخاصة بك لامدا/مندوب لكل عنصر في حاوية سوف تحصل على "ForEach" السلوك.

هنا على سبيل المثال طريقة دمج srcDictionary في destDictionary (إذا كان مفتاح موجود بالفعل - الكتابة)

هذا هو الإختراق, و لا ينبغي أن تستخدم في أي رمز الإنتاج.

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();

مستوحاة من جون السكيت ، لقد وجهت له حل مع التالية:

طريقة التمديد:

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
    foreach (var item in source)
    {
        var target = keySelector(item);
        applyBehavior(target);
    }
}

العميل:

var jobs = new List<Job>() 
    { 
        new Job { Id = "XAML Developer" }, 
        new Job { Id = "Assassin" }, 
        new Job { Id = "Narco Trafficker" }
    };

jobs.Execute(ApplyFilter, j => j.Id);

. . .

    public void ApplyFilter(string filterId)
    {
        Debug.WriteLine(filterId);
    }

ForEach يمكن أيضا أن تكون بالسلاسل, فقط وضعت مرة أخرى إلى pileline بعد العمل. تبقى بطلاقة


Employees.ForEach(e=>e.Act_A)
         .ForEach(e=>e.Act_B)
         .ForEach(e=>e.Act_C);

Orders  //just for demo
    .ForEach(o=> o.EmailBuyer() )
    .ForEach(o=> o.ProcessBilling() )
    .ForEach(o=> o.ProcessShipping());


//conditional
Employees
    .ForEach(e=> {  if(e.Salary<1000) e.Raise(0.10);})
    .ForEach(e=> {  if(e.Age   >70  ) e.Retire();});

وهو حريصة نسخة من التنفيذ.

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (T item in enu) action(item);
    return enu; // make action Chainable/Fluent
}

تحرير: a كسول الإصدار باستخدام العائد العودة ، مثل هذا.

public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (var item in enu)
    {
        action(item);
        yield return item;
    }
}

كسول الإصدار يحتاج إلى أن تتحقق ، ToList() على سبيل المثال, وإلا, لا يحدث شيء.انظر أدناه التعليقات كبيرة من ToolmakerSteve.

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

وأظل كل ForEach() و ForEachLazy() في مكتبتي.

وأنا أختلف respectually مع فكرة أن ربط طرق الإرشاد ينبغي أن تكون الآثار الجانبية مجانا (ليس فقط لأنها لم تكن كذلك، أي مندوب يمكن أن تؤدي آثار جانبية).

والنظر في المسائل التالية:

   public class Element {}

   public Enum ProcessType
   {
      This = 0, That = 1, SomethingElse = 2
   }

   public class Class1
   {
      private Dictionary<ProcessType, Action<Element>> actions = 
         new Dictionary<ProcessType,Action<Element>>();

      public Class1()
      {
         actions.Add( ProcessType.This, DoThis );
         actions.Add( ProcessType.That, DoThat );
         actions.Add( ProcessType.SomethingElse, DoSomethingElse );
      }

      // Element actions:

      // This example defines 3 distict actions
      // that can be applied to individual elements,
      // But for the sake of the argument, make
      // no assumption about how many distict
      // actions there may, and that there could
      // possibly be many more.

      public void DoThis( Element element )
      {
         // Do something to element
      }

      public void DoThat( Element element )
      {
         // Do something to element
      }

      public void DoSomethingElse( Element element )
      {
         // Do something to element
      }

      public void Apply( ProcessType processType, IEnumerable<Element> elements )
      {
         Action<Element> action = null;
         if( ! actions.TryGetValue( processType, out action ) )
            throw new ArgumentException("processType");
         foreach( element in elements ) 
            action(element);
      }
   }

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

لVB.NET يجب عليك استخدام:

listVariable.ForEach(Sub(i) i.Property = "Value")

ليبقى واحد يجيد يمكن استخدام مثل هذه الحيلة:

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();

وMoreLinq ديه IEnumerable<T>.ForEach ونصف طن من الملحقات المفيدة الأخرى. هو على الأرجح لا يستحق أخذ الاعتماد فقط لForEach، ولكن هناك الكثير من الاشياء المفيدة في هناك.

https://www.nuget.org/packages/morelinq/

https://github.com/morelinq/MoreLINQ

ولكن ForEach آخر مثال

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

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