Setara dengan LINQ untuk IEnumerable <T>


716

Saya ingin melakukan yang setara dengan yang berikut ini di LINQ, tapi saya tidak tahu caranya:

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

Apa sintaks yang sebenarnya?


99
Pertimbangkan "foreach" vs. "ForEach" oleh Eric Lippert. Ini memberikan pembenaran yang baik untuk tidak menggunakan / memasok a ForEach().

4
@ pst: Dulu saya membabi buta menempel metode ekstensi ini di proyek saya. Terima kasih untuk artikelnya.
Robin Maben

2
Ada MoreLINQ yang memiliki ForEachekstensi .
Uwe Keim

2
Meskipun tidak terlalu seksi, ini cocok pada satu baris tanpa membuat salinan melalui ToList -foreach (var i in items) i.Dostuff();
James Westgate

1
Beberapa tautan ini sudah mati! Artikel apa yang banyak dipilih!
Warren

Jawaban:


863

Tidak ada ekstensi ForEach untuk IEnumerable; hanya untuk List<T>. Jadi kamu bisa melakukannya

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

Atau, tulis metode ekstensi ForEach Anda sendiri:

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

213
Hati-hati dengan ToList (), karena ToList () membuat salinan urutan, yang dapat menyebabkan masalah kinerja dan memori.
decasteljau

15
Ada beberapa alasan mengapa ".Foreach ()" akan lebih baik. 1. Multi-threading, dimungkinkan oleh PLINQ. 2. Pengecualian, Anda mungkin ingin mengumpulkan semua pengecualian dalam loop dan membuangnya sekaligus; dan mereka adalah kode kebisingan.
Dennis C

12
PLINQ menggunakan ForAll not ForEach, itulah sebabnya Anda tidak akan menggunakan ForEach.
user7116

9
Anda harus mengembalikan sebuah IENumerable<T>dalam metode ekstensi. Seperti:public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Kees C. Bakker

22
Perhatikan bahwa dalam kasus pertama pengembang 1) menyimpan hampir tidak ada pengetikan dan 2) tidak perlu mengalokasikan memori untuk menyimpan seluruh urutan sebagai daftar sebelum membuangnya. Pikirkan sebelum Anda LINQ
Mark Sowul

364

Fredrik telah memberikan perbaikan, tetapi mungkin layak mempertimbangkan mengapa ini tidak dalam kerangka untuk memulai. Saya percaya idenya adalah bahwa operator kueri LINQ harus bebas efek samping, cocok dengan cara yang cukup fungsional dalam memandang dunia. Jelas ForEach justru sebaliknya - sebuah konstruksi berbasis efek samping murni .

Itu bukan untuk mengatakan ini adalah hal yang buruk untuk dilakukan - hanya memikirkan alasan filosofis di balik keputusan.


44
Namun, F # memiliki Seq.iter
Stephen Swensen

6
Namun, ada satu hal yang sangat berguna bahwa fungsi LINQ biasanya menyediakan foreach () tidak - fungsi anonim yang diambil oleh fungsi Linq juga dapat mengambil indeks sebagai parameter, sedangkan dengan foreach Anda harus membangun kembali klasik untuk (int .. ) membangun. Dan bahasa fungsional harus memungkinkan iterasi yang memiliki efek samping - jika tidak, Anda tidak akan pernah bisa bekerja sedikit pun dengan mereka :) Saya ingin menunjukkan bahwa metode fungsional yang tipikal adalah mengembalikan yang semula enumerable tidak berubah.
Walt W

3
@ Walt: Saya masih tidak yakin saya setuju, sebagian karena pendekatan yang benar-benar fungsional tidak akan menggunakan foreach seperti ini ... itu akan menggunakan pendekatan fungsional lebih seperti LINQ, menciptakan urutan baru.
Jon Skeet

12
@Stephen Swensen: ya, F # memiliki Seq.iter, tetapi F # juga memiliki struktur data yang tidak berubah. saat menggunakan Seq.iter pada ini, Seq asli tidak dimodifikasi; Seq baru dengan elemen efek samping dikembalikan.
Anders

7
@Anders - Seq.itertidak mengembalikan apa pun, apalagi seq baru dengan elemen efek samping. Ini hanya tentang efek samping. Anda mungkin berpikir Seq.map, karena Seq.itersama dengan ForEachmetode ekstensi aktif IEnumerabe<_>.
Joel Mueller

39

Pembaruan 7/17/2012: Rupanya pada C # 5.0, perilaku yang foreachdijelaskan di bawah ini telah diubah dan " penggunaan foreachvariabel iterasi dalam ekspresi lambda bersarang tidak lagi menghasilkan hasil yang tidak terduga. " Jawaban ini tidak berlaku untuk C # ≥ 5.0 .

@John Skeet dan semua orang yang lebih suka kata kunci foreach.

Masalah dengan "foreach" dalam C # sebelum 5.0 , adalah bahwa itu tidak konsisten dengan cara yang setara "untuk pemahaman" bekerja dalam bahasa lain, dan dengan bagaimana saya mengharapkannya bekerja (pendapat pribadi yang dinyatakan di sini hanya karena orang lain telah menyebutkan mereka pendapat tentang keterbacaan). Lihat semua pertanyaan tentang " Akses ke penutupan yang dimodifikasi " serta " Menutup variabel loop yang dianggap berbahaya ". Ini hanya "berbahaya" karena cara "foreach" diimplementasikan dalam C #.

Ambil contoh berikut menggunakan metode ekstensi yang setara secara fungsional dengan yang ada di jawaban @Fredrik Kalseth.

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

Permintaan maaf untuk contoh yang terlalu dibuat-buat. Saya hanya menggunakan Observable karena tidak sepenuhnya diambil untuk melakukan sesuatu seperti ini. Jelas ada cara yang lebih baik untuk membuat ini dapat diamati, saya hanya berusaha menunjukkan suatu hal. Biasanya kode yang berlangganan observable dijalankan secara tidak serempak dan berpotensi di utas lainnya. Jika menggunakan "foreach", ini bisa menghasilkan hasil yang sangat aneh dan berpotensi non-deterministik.

Tes berikut menggunakan metode ekstensi "ForEach" lolos:

[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()));
}

Berikut ini gagal dengan kesalahan:

Diharapkan: setara dengan <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Tetapi adalah: <9, 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()));
}

Apa kata peringatan penyelamat?
reggaeguitar

1
@reggaeguitar Saya belum pernah menggunakan C # dalam 7 atau 8 tahun, sejak sebelum C # 5.0 dirilis, yang mengubah perilaku yang dijelaskan di sini. Pada saat itu ia memberi peringatan ini stackoverflow.com/questions/1688465/… . Saya ragu bahwa ini masih memperingatkan tentang ini.
drstevens

34

Anda dapat menggunakan FirstOrDefault()ekstensi yang tersedia untuk IEnumerable<T>. Dengan kembali falsedari predikat, itu akan dijalankan untuk setiap elemen tetapi tidak akan peduli bahwa itu tidak benar-benar menemukan kecocokan. Ini akan menghindari ToList()overhead.

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

11
Saya juga setuju. Ini mungkin hack kecil yang pintar, tetapi pada pandangan pertama, kode ini tidak jelas apa yang dilakukannya, tentu dibandingkan dengan loop foreach standar.
Connell

26
Saya harus mengundurkan diri karena walaupun secara teknis benar, masa depan Anda sendiri dan semua penerus Anda akan membenci Anda selamanya karena alih-alih foreach(Item i in GetItems()) { i.DoStuff();}Anda mengambil lebih banyak karakter dan membuatnya sangat membingungkan
Mark Sowul

14
Ok, tapi prolly hack yang lebih mudah dibaca:items.All(i => { i.DoStuff(); return true; }
nawfal

5
Saya tahu ini adalah posting yang cukup lama, tetapi saya harus mengunduhnya juga. Saya setuju itu trik yang cerdik, tapi itu tidak bisa dipertahankan. Sepintas sepertinya melakukan sesuatu dengan item pertama dalam daftar saja. Ini dapat dengan mudah menyebabkan lebih banyak bug di masa depan oleh coders yang salah paham bagaimana fungsinya dan apa yang seharusnya dilakukan.
Lee

4
Ini adalah pemrograman efek samping dan IMHO tidak disarankan.
Anish

21

Saya mengambil metode Fredrik dan memodifikasi tipe pengembalian.

Dengan cara ini, metode ini mendukung eksekusi yang ditangguhkan seperti metode LINQ lainnya.

EDIT: Jika ini tidak jelas, segala penggunaan metode ini harus diakhiri dengan ToList () atau cara lain untuk memaksa metode untuk bekerja pada enumerable lengkap. Jika tidak, tindakan tidak akan dilakukan!

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

Dan inilah tes untuk membantu melihatnya:

[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"));
}

Jika Anda menghapus ToList () pada akhirnya, Anda akan melihat tes gagal karena StringBuilder berisi string kosong. Ini karena tidak ada metode yang memaksa ForEach untuk menghitung.


Implementasi alternatif Anda ForEachmenarik, namun tidak sesuai dengan perilaku List.ForEach, yang merupakan tanda tangannya public void ForEach(Action<T> action). Itu juga tidak cocok dengan perilaku Observable.ForEachekstensi untuk IObservable<T>, tanda tangan yang public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext). FWIW, ForEachsetara pada koleksi Scala, bahkan yang malas, juga memiliki tipe pengembalian yang setara dengan batal dalam C #.
drstevens

Ini adalah jawaban terbaik: eksekusi ditunda + api lancar.
Alexander Danilov

3
Ini berfungsi persis sama dengan menelepon Selectdengan ToList. Tujuannya ForEachadalah untuk tidak harus menelepon ToList. Itu harus dijalankan segera.
t3chb0t

3
Apa manfaat dari memiliki "ForEach" ini, yang eksekusinya ditunda, vs yang dieksekusi segera? Menunda (yang tidak nyaman dalam bagaimana ForEach akan digunakan) tidak memperbaiki fakta bahwa ForEach hanya melakukan pekerjaan yang bermanfaat jika Action memiliki efek samping [keluhan yang biasa dibuat terhadap operator Linq tersebut]. Jadi, bagaimana melakukan perubahan ini membantu? Selain itu, "ToList ()" yang ditambahkan untuk memaksa dijalankan, tidak ada gunanya. Ini kecelakaan yang menunggu untuk terjadi. Inti dari "ForEach" adalah efek sampingnya. Jika Anda INGIN enumerasi yang dikembalikan, SELECT lebih masuk akal.
ToolmakerSteve

20

Jauhkan Efek Samping Anda dari IEnumerable saya

Saya ingin melakukan yang setara dengan yang berikut ini di LINQ, tapi saya tidak tahu caranya:

Seperti yang orang lain tunjukkan di sini dan di luar negeri, LINQ danIEnumerable metode diharapkan bebas efek samping.

Apakah Anda benar-benar ingin "melakukan sesuatu" untuk setiap item di IEnumerable? Maka foreachmerupakan pilihan terbaik. Orang-orang tidak terkejut ketika efek samping terjadi di sini.

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

Saya yakin Anda tidak ingin efek samping

Namun dalam pengalaman saya efek samping biasanya tidak diperlukan. Lebih sering daripada tidak ada permintaan LINQ sederhana menunggu untuk ditemukan disertai dengan jawaban StackOverflow.com oleh Jon Skeet, Eric Lippert, atau Marc Gravell menjelaskan bagaimana melakukan apa yang Anda inginkan!

Beberapa contoh

Jika Anda sebenarnya hanya mengagregasi (mengakumulasi) beberapa nilai maka Anda harus mempertimbangkan Aggregatemetode ekstensi.

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

Mungkin Anda ingin membuat yang baru IEnumerabledari nilai yang ada.

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

Atau mungkin Anda ingin membuat tabel pencarian:

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

Daftar (pun kata yang tidak sepenuhnya dimaksudkan) tentang kemungkinan berlanjut dan terus.


7
Ah, jadi Pilih adalah Peta, dan Agregat adalah Mengurangi.
Darrel Lee

17

Jika Anda ingin bertindak sebagai gulungan enumerasi, Anda harus menghasilkan setiap item.

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;
        }
    }
}

Ini bukan benar-benar ForEach ketika Anda mengembalikan item, itu lebih seperti Tap.
EluciusFTW

10

Ada rilis eksperimental oleh Microsoft untuk Interactive Extensions to LINQ (juga di NuGet , lihat profil RxTeams untuk tautan lainnya). Video Channel 9 menjelaskannya dengan baik.

Dokumennya hanya disediakan dalam format XML. Saya telah menjalankan dokumentasi ini di Sandcastle untuk membuatnya dalam format yang lebih mudah dibaca. Buka zip arsip docs dan cari index.html .

Di antara banyak barang lainnya, ini menyediakan implementasi ForEach yang diharapkan. Anda dapat menulis kode seperti ini:

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

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

2
Tautan yang berfungsi untuk Ekstensi Interaktif: microsoft.com/download/en/details.aspx?id=27203
Maciek Świszczowski

8

Menurut PLINQ (tersedia sejak .Net 4.0), Anda dapat melakukan

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

untuk melakukan loop foreach paralel pada IEnumerable.


selama tindakan Anda aman, semuanya baik. tetapi apa yang akan terjadi jika Anda memiliki tindakan yang tidak aman untuk dilakukan? itu bisa menimbulkan kekacauan ...
shirbr510

2
Benar. Itu bukan thread aman, utas berbeda akan digunakan dari beberapa utas thread ... Dan saya perlu merevisi jawaban saya. Tidak ada ForEach () ketika saya mencobanya sekarang .. Pasti kode saya berisi ekstensi dengan ForEach ketika saya merenungkan hal ini.
Wolf5

6

Tujuan dari ForEach adalah untuk menimbulkan efek samping. IEnumerable adalah untuk enumerasi satu set yang malas.

Perbedaan konseptual ini cukup terlihat ketika Anda mempertimbangkannya.

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

Ini tidak akan dijalankan sampai Anda melakukan "menghitung" atau "Daftar ()" atau sesuatu di atasnya. Jelas bukan apa yang diungkapkan.

Anda harus menggunakan ekstensi IEnumerable untuk menyiapkan rantai iterasi, menentukan konten berdasarkan sumber dan ketentuan masing-masing. Pohon Ekspresi kuat dan efisien, tetapi Anda harus belajar untuk menghargai sifat mereka. Dan tidak hanya untuk pemrograman di sekitar mereka untuk menyimpan beberapa karakter yang mengesampingkan evaluasi malas.


5

Banyak orang yang menyebutkannya, tetapi saya harus menuliskannya. Bukankah ini paling jelas / paling mudah dibaca?

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

Singkat dan sederhana.


Anda dapat menghapus seluruh baris pertama dan menggunakan GetItems langsung di foreach
tymtam

3
Tentu saja. Ini ditulis seperti ini untuk kejelasan. Sehingga jelas GetItems()metode apa yang dikembalikan.
Nenad

4
Sekarang ketika saya membaca komentar saya, sepertinya saya mengatakan sesuatu yang tidak Anda ketahui. Saya tidak bermaksud begitu. Yang saya maksud adalah itu foreach (var item in GetItems()) item.DoStuff();hanya keindahan.
tymtam

4

Sekarang kami memiliki opsi untuk ...

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

Tentu saja, ini membuka kaleng ulat baru.

ps (Maaf tentang font, itu yang diputuskan sistem)


4

Seperti yang sudah ditunjukkan oleh banyak jawaban, Anda dapat dengan mudah menambahkan sendiri metode perluasan semacam itu. Namun, jika Anda tidak ingin melakukan itu, meskipun saya tidak mengetahui hal seperti ini di BCL, masih ada opsi di Systemnamespace, jika Anda sudah memiliki referensi ke Reactive Extension (dan jika Anda tidak , Kamu harus punya):

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

Meskipun nama metode sedikit berbeda, hasil akhirnya adalah persis apa yang Anda cari.


Sepertinya paket itu sudah tidak digunakan lagi
reggaeguitar

3

ForEach juga dapat Dirantai , cukup kembalikan ke pileline setelah tindakan. tetap fasih


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();});

Sebuah Bersemangat versi implementasi.

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
}

Edit: a Malas versi menggunakan yield return, seperti ini .

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

Versi Malas PERLU terwujud, ToList () misalnya, jika tidak, tidak ada yang terjadi. lihat di bawah komentar hebat dari ToolmakerSteve.

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

Saya menyimpan ForEach () dan ForEachLazy () di perpustakaan saya.


2
Sudahkah Anda menguji ini? Ada beberapa konteks di mana metode ini berperilaku contra secara intuitif. Lebih baikforeach(T item in enu) {action(item); yield return item;}
Taemyr

2
Ini akan menghasilkan banyak enumerasi
regisbsb

Menarik. Merenungkan ketika saya ingin melakukan hal di atas, untuk urutan 3 tindakan, bukan foreach (T item in enu) { action1(item); action2(item); action3(item); }? Lingkaran tunggal, eksplisit,. Saya kira jika itu penting untuk melakukan semua action1 SEBELUM mulai melakukan action2.
ToolmakerSteve

@ Rm558 - pendekatan Anda menggunakan "imbal hasil". Pro: hanya menghitung urutan asli sekali, jadi berfungsi dengan benar dengan rentang enumerasi yang lebih luas. Con: jika Anda lupa ".ToArray ()" pada akhirnya, tidak ada yang terjadi. Padahal kegagalannya akan terlihat jelas, tidak terabaikan lama-lama, jadi bukan biaya besar. Tidak "merasa bersih" bagi saya, tetapi merupakan tradeoff yang tepat, jika seseorang menyusuri jalan ini (operator ForEach).
ToolmakerSteve

1
Kode ini tidak aman dari sudut pandang transaksional. Bagaimana jika gagal ProcessShipping()? Anda mengirim email kepada pembeli, menagih kartu kreditnya, tetapi tidak pernah mengirim barang kepadanya? Pastinya menanyakan masalah.
zmechanic

2

Abstraksi "pendekatan fungsional" ini bocor banyak waktu. Tidak ada pada tingkat bahasa yang mencegah efek samping. Selama Anda bisa membuatnya memanggil lambda / delegasi Anda untuk setiap elemen dalam wadah - Anda akan mendapatkan perilaku "ForEach".

Di sini misalnya satu cara menggabungkan srcDictionary ke destDictionary (jika kunci sudah ada - ditimpa)

ini adalah hack, dan tidak boleh digunakan dalam kode produksi apa pun.

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

6
Jika Anda harus menambahkan penafian itu, Anda mungkin tidak boleh mempostingnya. Hanya pendapat saya saja.
Andrew Grothe

2
Bagaimana ini lebih baik daripada foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }? Jika tidak dalam kode produksi di mana dan mengapa Anda harus menggunakan ini?
Mark Sowul

3
Ini hanya untuk menunjukkan bahwa secara prinsip dimungkinkan. Kebetulan juga melakukan apa yang diminta OP, dan saya jelas menunjukkan itu tidak boleh digunakan dalam produksi, masih penasaran dan memiliki nilai pendidikan.
Zar Shardan

2

Terinspirasi oleh Jon Skeet, saya telah memperluas solusinya dengan yang berikut:

Metode ekstensi:

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);
    }
}

Klien:

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);
    }


1

Saya dengan hormat tidak setuju dengan gagasan bahwa metode ekstensi tautan harus bebas efek samping (bukan hanya karena tidak, delegasi mana pun dapat melakukan efek samping).

Pertimbangkan yang berikut ini:

   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);
      }
   }

Apa yang ditunjukkan contoh ini adalah benar-benar semacam ikatan yang memungkinkan seseorang memanggil salah satu dari banyak tindakan yang mungkin memiliki efek samping pada urutan elemen, tanpa harus menulis konstruksi saklar besar untuk memecahkan kode nilai yang mendefinisikan tindakan dan menerjemahkan ke dalam metode yang sesuai.


9
Ada perbedaan antara apakah metode ekstensi DAPAT memiliki efek samping dan apakah itu HARUS miliki. Anda hanya menunjukkan bahwa itu dapat memiliki, sementara mungkin ada alasan yang sangat bagus untuk mengusulkan bahwa mereka seharusnya tidak memiliki efek samping. Dalam pemrograman fungsional, semua fungsi bebas efek samping, dan saat menggunakan konstruksi pemrograman fungsional, Anda mungkin ingin mengasumsikannya.
Dave Van den Eynde

1

Agar tetap lancar, seseorang dapat menggunakan trik seperti ini:

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


-1

ForEachContoh lain lagi

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;
}

4
Sungguh buang-buang memori. Ini memanggil ToList untuk ditambahkan ke daftar. Mengapa ini tidak mengembalikan alamat saja. Pilih (a => MapToDomain (a))?
Mark Sowul
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.