Mengapa tidak ada metode ekstensi ForEach di IEnumerable?

389

Terinspirasi oleh pertanyaan lain yang menanyakan tentang Zipfungsi yang hilang :

Mengapa tidak ada ForEachmetode ekstensi di Enumerablekelas? Atau dimana saja? Satu-satunya kelas yang mendapat ForEachmetode adalah List<>. Apakah ada alasan mengapa itu hilang (kinerja)?

Cameron MacFarland
sumber
Ini mungkin seperti yang diisyaratkan oleh posting sebelumnya, dalam foreach didukung sebagai kata kunci bahasa dalam C # dan VB.NET (dan banyak lainnya) Kebanyakan orang (termasuk saya) hanya menulis satu sendiri sesuai kebutuhan dan sesuai.
chrisb
2
Pertanyaan terkait di sini dengan tautan ke 'jawaban resmi' stackoverflow.com/questions/858978/…
Benjol
1
Lihat juga stackoverflow.com/questions/200574/…
selamat tinggal
Saya pikir satu hal yang sangat penting adalah, bahwa foreach loop lebih mudah dikenali. Jika Anda menggunakan .ForEach (...) mudah untuk melewatkan yang satu ini, ketika Anda melihat melalui kode. Ini menjadi penting, ketika Anda memiliki masalah kinerja. Tentu saja .ToList () dan .ToArray () memiliki masalah yang sama tetapi mereka digunakan sedikit berbeda. Alasan lain bisa jadi itu lebih umum di. ForEach untuk memodifikasi daftar sumber (fe menghapus / menambahkan elemen) sehingga tidak akan menjadi permintaan "murni" lagi.
Daniel Bişar
Ketika debugging kode parallelised, saya sering mencoba menukar Parallel.ForEachuntuk Enumerable.ForEachhanya untuk menemukan kembali yang terakhir tidak ada. C # melewatkan trik untuk mempermudah di sini.
Kolonel Panic

Jawaban:

198

Sudah ada pernyataan pendahuluan termasuk dalam bahasa yang melakukan pekerjaan sebagian besar waktu.

Aku benci melihat yang berikut ini:

list.ForEach( item =>
{
    item.DoSomething();
} );

Dari pada:

foreach(Item item in list)
{
     item.DoSomething();
}

Yang terakhir ini lebih jelas dan lebih mudah dibaca di sebagian besar situasi , meskipun mungkin sedikit lebih lama untuk mengetik.

Namun, saya harus mengakui bahwa saya mengubah pendirian saya tentang masalah itu; metode ekstensi ForEach () memang akan berguna dalam beberapa situasi.

Berikut adalah perbedaan utama antara pernyataan dan metode:

  • Pengecekan tipe: foreach dilakukan pada saat runtime, ForEach () berada pada waktu kompilasi (Plus Besar!)
  • Sintaks untuk memanggil delegasi memang lebih sederhana: objek.ForEach (DoSomething);
  • ForEach () dapat dirantai: meskipun kejahatan / kegunaan fitur tersebut terbuka untuk diskusi.

Itu semua adalah poin bagus yang dibuat oleh banyak orang di sini dan saya dapat melihat mengapa orang kehilangan fungsi. Saya tidak keberatan Microsoft menambahkan metode ForEach standar dalam iterasi kerangka kerja berikutnya.

Coincoin
sumber
39
Diskusi di sini memberikan jawabannya: forums.microsoft.com/MSDN/... Pada dasarnya keputusan dibuat untuk menjaga metode ekstensi secara fungsional "murni". ForEach akan mendorong efek samping saat menggunakan metode ekstensi, yang bukan maksudnya.
mancaus
17
'foreach' mungkin tampak lebih jelas jika Anda memiliki latar belakang C, tetapi iterasi internal lebih jelas menyatakan niat Anda. Itu berarti Anda bisa melakukan hal-hal seperti 'urutan. Asal (). ForEach (...)'.
Jay Bazuzi
10
Saya tidak yakin poin Anda tentang pemeriksaan tipe sudah benar. foreachadalah tipe yang diperiksa pada waktu kompilasi jika enumerable di-parameterisasi. Itu hanya kurang mengkompilasi tipe waktu memeriksa untuk koleksi non-generik, seperti halnya ForEachmetode ekstensi hipotetis .
Richard Poole
49
Metode penyuluhan akan sangat berguna dalam rantai LINQ dengan notasi titik, seperti: col.Where(...).OrderBy(...).ForEach(...). Sintaksnya jauh lebih sederhana, tidak ada polusi layar dengan variabel lokal berlebihan atau tanda kurung panjang di foreach ().
Jacek Gorgoń
22
"Aku benci melihat yang berikut" - Pertanyaannya bukan tentang preferensi pribadi Anda, dan Anda membuat kode lebih kebencian dengan menambahkan umpan garis asing dan sepasang kawat gigi asing. Tidak begitu penuh kebencian list.ForEach(item => item.DoSomething()). Dan siapa bilang daftar? Kasus penggunaan yang jelas adalah pada akhir rantai; dengan pernyataan pendahuluan, Anda harus menempatkan seluruh rantai setelahnya in, dan operasi untuk dilakukan di dalam blok, yang jauh kurang alami atau konsisten.
Jim Balter
73

Metode ForEach ditambahkan sebelum LINQ. Jika Anda menambahkan ekstensi ForEach, itu tidak akan pernah dipanggil untuk instance Daftar karena kendala metode ekstensi. Saya pikir alasannya tidak ditambahkan adalah untuk tidak mengganggu yang sudah ada.

Namun, jika Anda benar-benar kehilangan fungsi kecil yang menyenangkan ini, Anda dapat meluncurkan versi Anda sendiri

public static void ForEach<T>(
    this IEnumerable<T> source,
    Action<T> action)
{
    foreach (T element in source) 
        action(element);
}
aku
sumber
11
Metode ekstensi memungkinkan untuk ini. Jika sudah ada contoh implementasi nama metode yang diberikan maka metode yang digunakan bukan metode ekstensi. Jadi, Anda dapat menerapkan metode ekstensi ForEach dan tidak memilikinya berbenturan dengan metode Daftar <>. ForEach.
Cameron MacFarland
3
Cameron, percayalah, saya tahu cara kerja metode ekstensi :) Daftar mewarisi IEnumerable sehingga akan menjadi hal yang tidak jelas.
aku
9
Dari Panduan Pemrograman Metode Ekstensi ( msdn.microsoft.com/en-us/library/bb383977.aspx ): Metode ekstensi dengan nama dan tanda tangan yang sama dengan antarmuka atau metode kelas tidak akan pernah dipanggil. Tampak jelas bagi saya.
Cameron MacFarland
3
Apakah saya berbicara sesuatu yang berbeda? Saya pikir itu tidak baik ketika banyak kelas memiliki metode ForEach tetapi beberapa dari mereka mungkin bekerja secara berbeda.
aku
5
Satu hal yang menarik untuk dicatat tentang Daftar <>. ForEach dan Array. ForEach adalah bahwa mereka tidak benar-benar menggunakan konstruksi foreach secara internal. Keduanya menggunakan plain untuk loop. Mungkin itu hal kinerja ( diditwith.net/2006/10/05/PerformanceOfForeachVsListForEach.aspx ). Tetapi mengingat bahwa, Array dan Daftar keduanya menerapkan metode ForEach, mengejutkan bahwa mereka setidaknya tidak menerapkan metode ekstensi untuk IList <>, jika bukan untuk IEnumerable <> juga.
Matt Dotson
53

Anda dapat menulis metode ekstensi ini:

// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
    foreach (var e in source)
    {
        action(e);
        yield return e;
    }
}

Pro

Memungkinkan chaining:

MySequence
    .Apply(...)
    .Apply(...)
    .Apply(...);

Cons

Ini tidak akan benar-benar melakukan apa pun sampai Anda melakukan sesuatu untuk memaksa iterasi. Karena itu, itu tidak boleh disebut .ForEach(). Anda bisa menulis .ToList()di akhir, atau menulis metode ekstensi ini juga:

// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
    foreach (var e in source)
    {
        // do nothing
        ;
    }

    return source;
}

Ini mungkin terlalu signifikan keberangkatan dari perpustakaan C # pengiriman; pembaca yang tidak terbiasa dengan metode ekstensi Anda tidak akan tahu apa yang membuat kode Anda.

Jay Bazuzi
sumber
1
Fungsi pertama Anda dapat digunakan tanpa metode 'sadari' jika ditulis seperti:{foreach (var e in source) {action(e);} return source;}
jjnguy
3
Tidak bisakah Anda hanya menggunakan Pilih daripada metode Terapkan Anda? Bagi saya, menerapkan tindakan pada setiap elemen dan menghasilkannya persis seperti yang dilakukan Select. Misalnyanumbers.Select(n => n*2);
rasmusvhansen
1
Saya pikir Terapkan lebih mudah dibaca daripada menggunakan Pilih untuk tujuan ini. Saat membaca pernyataan Pilih, saya membacanya sebagai "dapatkan sesuatu dari dalam" di mana Terapkan adalah "lakukan beberapa pekerjaan".
Dr Rob Lang
2
@rasmusvhansen Sebenarnya, Select()kata menerapkan fungsi ke setiap elemen dan mengembalikan nilai fungsi di mana Apply()mengatakan menerapkan sebuah Actionke setiap elemen dan mengembalikan elemen asli .
NetMage
1
Ini setara denganSelect(e => { action(e); return e; })
Jim Balter
35

Diskusi di sini memberikan jawabannya:

Sebenarnya, diskusi spesifik yang saya saksikan ternyata bergantung pada kemurnian fungsional. Dalam sebuah ekspresi, sering ada asumsi yang dibuat tentang tidak memiliki efek samping. Memiliki ForEach secara khusus mengundang efek samping daripada hanya bertahan dengan mereka. - Petani Keith (Mitra)

Pada dasarnya keputusan dibuat untuk menjaga metode penyuluhan fungsional "murni". ForEach akan mendorong efek samping saat menggunakan metode ekstensi Enumerable, yang bukan tujuannya.

mancaus
sumber
6
Lihat juga blogs.msdn.com/b/ericlippert/archive/2009/05/18/… . Melakukan hal itu melanggar prinsip pemrograman fungsional yang menjadi dasar semua operator urutan lainnya. Jelas satu-satunya tujuan panggilan ke metode ini adalah untuk menimbulkan efek samping
Michael Freidgeim
7
Alasan itu sepenuhnya palsu. Kasus penggunaannya adalah bahwa efek samping diperlukan ... tanpa ForEach, Anda harus menulis loop foreach. Keberadaan ForEach tidak mendorong efek samping dan ketidakhadirannya tidak mengurangi efek sampingnya.
Jim Balter
Mengingat betapa sederhananya untuk menulis metode ekstensi, secara harfiah tidak ada alasan bahwa itu bukan standar bahasa.
Kapten Prinny
17

Sementara saya setuju bahwa lebih baik menggunakan built-in foreachconstruct dalam banyak kasus, saya menemukan penggunaan variasi ini pada ekstensi <Enach <> menjadi sedikit lebih baik daripada harus mengelola indeks secara teratur foreach:

public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
{
    if (action == null) throw new ArgumentNullException("action");

    var index = 0;

    foreach (var elem in list)
        action(index++, elem);

    return index;
}
Contoh
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));

Akan memberi Anda:

Person #0 is Moe
Person #1 is Curly
Person #2 is Larry
Chris Zwiryk
sumber
Ekstensi hebat, tetapi bisakah Anda menambahkan beberapa dokumentasi? Apa tujuan penggunaan nilai yang dikembalikan? Mengapa indeks var bukan khusus int? Penggunaan sampel?
Mark T
Mark: Nilai kembali adalah jumlah elemen dalam daftar. Indeks ini secara khusus diketik sebagai int, berkat mengetik implisit.
Chris Zwiryk
@ Chris, berdasarkan kode Anda di atas, nilai kembali adalah indeks berbasis nol dari nilai terakhir dalam daftar, bukan jumlah elemen dalam daftar.
Eric Smith
@ Chris, bukan itu: indeks bertambah setelah nilai diambil (kenaikan postfix), jadi setelah elemen pertama diproses, indeks akan menjadi 1 dan seterusnya. Jadi nilai kembali sebenarnya adalah jumlah elemen.
MartinStettner
1
@MartinStettner komentar dari Eric Smith pada 5/16 berasal dari versi sebelumnya. Dia mengoreksi kode pada 5/18 untuk mengembalikan nilai yang benar.
Michael Blackburn
16

Salah satu solusinya adalah menulis .ToList().ForEach(x => ...).

pro

Mudah dimengerti - pembaca hanya perlu tahu apa yang dikirim dengan C #, bukan metode ekstensi tambahan.

Kebisingan sintaksis sangat ringan (hanya menambahkan sedikit kode tambahan).

Biasanya tidak memerlukan memori ekstra, karena penduduk asli .ForEach()harus menyadari seluruh koleksinya.

kontra

Urutan operasi tidak ideal. Saya lebih suka menyadari satu elemen, lalu menindaklanjutinya, lalu ulangi. Kode ini menyadari semua elemen terlebih dahulu, kemudian bertindak masing-masing secara berurutan.

Jika menyadari daftar melempar pengecualian, Anda tidak pernah bisa bertindak pada satu elemen.

Jika enumerasi tidak terbatas (seperti angka alami), Anda kurang beruntung.

Jay Bazuzi
sumber
1
a) Ini bahkan bukan jawaban untuk pertanyaan dari jarak jauh. b) Tanggapan ini akan lebih jelas jika disebutkan di muka bahwa, sementara tidak ada Enumerable.ForEach<T>metode, ada List<T>.ForEachmetode. c) "Biasanya tidak memerlukan memori tambahan, karena asli. Untuk setiap () harus merealisasikan seluruh koleksi, pula." - lengkap dan omong kosong. Berikut ini adalah implementasi dari Enumerable.ForEach <T> yang akan disediakan oleh C # jika itu terjadi:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Jim Balter
13

Saya selalu bertanya-tanya sendiri, itulah sebabnya saya selalu membawa ini:

public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
    if (action == null)
    {
        throw new ArgumentNullException("action");
    }
    foreach (var item in col)
    {
        action(item);
    }
}

Metode ekstensi kecil yang bagus.

Aaron Powell
sumber
2
col bisa nol ... Anda bisa memeriksanya juga.
Amy B
Ya saya memeriksa di perpustakaan saya, saya hanya melewatkannya ketika melakukannya dengan cepat di sana.
Aaron Powell
Saya merasa menarik bahwa semua orang memeriksa parameter tindakan untuk null, tetapi tidak pernah parameter sumber / col.
Cameron MacFarland
2
Saya merasa menarik bahwa semua orang memeriksa parameter untuk null, ketika tidak memeriksa hasil dalam pengecualian juga ...
o
Tidak semuanya. Pengecualian Null Ref tidak akan memberi tahu Anda nama parameter yang salah. Dan Anda juga dapat memeriksa di loop sehingga Anda bisa melempar Null Ref tertentu yang mengatakan col [index #] adalah null karena alasan yang sama
Roger Willcocks
8

Jadi ada banyak komentar tentang fakta bahwa metode ekstensi ForEach tidak tepat karena tidak mengembalikan nilai seperti metode ekstensi LINQ. Meskipun ini adalah pernyataan faktual, itu tidak sepenuhnya benar.

Metode ekstensi LINQ melakukan semua mengembalikan nilai sehingga mereka dapat dirantai bersama:

collection.Where(i => i.Name = "hello").Select(i => i.FullName);

Namun, hanya karena LINQ diimplementasikan menggunakan metode ekstensi tidak berarti metode ekstensi harus digunakan dengan cara yang sama dan mengembalikan nilai. Menulis metode ekstensi untuk mengekspos fungsionalitas umum yang tidak mengembalikan nilai adalah penggunaan yang benar-benar valid.

Argumen khusus tentang ForEach adalah bahwa, berdasarkan batasan pada metode ekstensi (yaitu bahwa metode ekstensi tidak akan pernah menimpa metode yang diwariskan dengan tanda tangan yang sama ), mungkin ada situasi di mana metode ekstensi kustom tersedia di semua kelas yang menerapkan Tidak dapat dihitung <T> kecuali Daftar <T>. Ini dapat menyebabkan kebingungan ketika metode mulai berperilaku berbeda tergantung pada apakah metode ekstensi atau metode warisan dipanggil.

Scott Dorman
sumber
7

Anda dapat menggunakan (dapat ditagih, tetapi dievaluasi malas) Select, pertama melakukan operasi Anda, dan kemudian mengembalikan identitas (atau sesuatu yang lain jika Anda suka)

IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });

Anda harus memastikan itu masih dievaluasi, baik dengan Count()(operasi termurah untuk menyebutkan afaik) atau operasi lain yang Anda perlukan.

Saya ingin melihatnya dibawa ke perpustakaan standar:

static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
  return src.Select(i => { action(i); return i; } );
}

Kode di atas kemudian menjadi people.WithLazySideEffect(p => Console.WriteLine(p))yang secara efektif setara dengan foreach, tetapi malas dan rantai.

Martijn
sumber
Fantastis, tidak pernah diklik bahwa pilih kembali akan bekerja seperti ini. Penggunaan sintaks metode yang bagus, karena saya tidak berpikir Anda bisa melakukan ini dengan sintaks ekspresi (karenanya saya tidak pernah memikirkannya seperti ini sebelumnya).
Alex KeySmith
@AlexKeySmith Anda dapat melakukannya dengan sintaks ekspresi jika C # memiliki operator urutan, seperti leluhurnya. Tetapi inti dari ForEach adalah mengambil tindakan dengan tipe void, dan Anda tidak bisa menggunakan tipe void dalam ekspresi dalam C #.
Jim Balter
imho, sekarang Selecttidak melakukan lagi apa yang seharusnya dilakukan. itu jelas merupakan solusi untuk apa yang diminta OP - tetapi pada keterbacaan topik: tidak seorang pun akan berharap bahwa pemilih menjalankan fungsi.
Matthias Burger
@MatthiasBurger Jika Selecttidak melakukan apa yang seharusnya dilakukan itu adalah masalah dengan implementasi Select, bukan dengan kode yang memanggil Select, yang tidak memiliki pengaruh terhadap apa yang Selectdilakukannya.
Martijn
4

@Coincoin

Kekuatan sebenarnya dari metode ekstensi foreach melibatkan penggunaan kembali Action<>tanpa menambahkan metode yang tidak perlu ke kode Anda. Katakanlah Anda memiliki 10 daftar dan Anda ingin melakukan logika yang sama pada mereka, dan fungsi yang sesuai tidak masuk ke dalam kelas Anda dan tidak digunakan kembali. Alih-alih memiliki sepuluh untuk loop, atau fungsi generik yang jelas-jelas merupakan helper yang bukan milik, Anda dapat menyimpan semua logika Anda di satu tempat (the Action<>. Jadi, puluhan baris diganti dengan

Action<blah,blah> f = { foo };

List1.ForEach(p => f(p))
List2.ForEach(p => f(p))

dll ...

Logikanya ada di satu tempat dan Anda belum mencemari kelas Anda.

spoulson
sumber
foreach (var p dalam List1.Concat (List2) .Concat (List3)) {... lakukan hal-hal ...}
Cameron MacFarland
Jika itu yang sederhana seperti f(p), kemudian meninggalkan keluar { }untuk singkatan: for (var p in List1.Concat(List2).Concat(List2)) f(p);.
spoulson
3

Sebagian besar metode ekstensi LINQ mengembalikan hasil. ForEach tidak cocok dengan pola ini karena tidak mengembalikan apa pun.

leppie
sumber
2
ForEach menggunakan Action <T>, yang merupakan bentuk lain dari delegasi
Aaron Powell
2
Kecuali hak leppie, semua metode ekstensi Enumerable mengembalikan nilai, jadi ForEach tidak cocok dengan pola. Delegasi tidak terlibat dalam hal ini.
Cameron MacFarland
Hanya saja metode ekstensi tidak harus mengembalikan hasil, itu melayani tujuan menambahkan cara untuk memanggil operasi yang sering digunakan
Aaron Powell
1
Memang, Slace. Jadi bagaimana jika itu tidak mengembalikan nilai?
TraumaPony
1
@Martijn iepublic IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
McKay
3

Jika Anda memiliki F # (yang akan berada dalam versi .NET berikutnya), Anda dapat menggunakan

Seq.iter doSomething myIEnumerable

TraumaPony
sumber
9
Jadi Microsoft memilih untuk tidak menyediakan metode ForEach untuk IEnumerable dalam C # dan VB, karena itu tidak akan berfungsi murni; namun mereka TIDAK memberikannya (nama iter) dalam bahasa yang lebih fungsional, F #. Logika mereka menyinggung saya.
Scott Hutchinson
3

Sebagian karena desainer bahasa tidak setuju dengan itu dari perspektif filosofis.

  • Tidak memiliki (dan menguji ...) fitur kurang berfungsi daripada memiliki fitur.
  • Ini tidak terlalu pendek (ada beberapa kasus fungsi yang lewat di mana itu, tapi itu tidak akan menjadi penggunaan utama).
  • Tujuannya adalah untuk memiliki efek samping, yang bukan tentang LINQ.
  • Mengapa ada cara lain untuk melakukan hal yang sama dengan fitur yang sudah kita miliki? (kata kunci foreach)

https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/

McKay
sumber
2

Anda dapat menggunakan pilih ketika Anda ingin mengembalikan sesuatu. Jika tidak, Anda dapat menggunakan ToList terlebih dahulu, karena Anda mungkin tidak ingin mengubah apa pun dalam koleksi.

Paco
sumber
a) Ini bukan jawaban untuk pertanyaan itu. b) ToList membutuhkan waktu dan memori ekstra.
Jim Balter
2

Saya menulis posting blog tentang hal itu: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx

Anda dapat memberikan suara di sini jika Anda ingin melihat metode ini dalam .NET 4.0: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093

Kirill Osenkov
sumber
apakah ini pernah diterapkan? Saya kira tidak, tetapi apakah ada URL lain yang menggantikan tiket itu? MS connect telah dihentikan
knocte
Ini tidak diterapkan. Pertimbangkan mengajukan masalah baru di github.com/dotnet/runtime/issues
Kirill Osenkov
2

Dalam 3.5, semua metode ekstensi ditambahkan ke IEnumerable ada untuk dukungan LINQ (perhatikan bahwa mereka didefinisikan dalam kelas System.Linq.Enumerable). Dalam posting ini, saya menjelaskan mengapa foreach tidak termasuk dalam LINQ: Metode ekstensi LINQ yang ada mirip dengan Parallel.For?

Neil
sumber
2

Apakah saya atau Daftar <T> .Foreach sudah dibuat usang oleh Linq. Awalnya ada

foreach(X x in Y) 

di mana Y hanya harus IEnumerable (Pre 2.0), dan mengimplementasikan GetEnumerator (). Jika Anda melihat MSIL yang dihasilkan, Anda dapat melihat bahwa itu persis sama dengan

IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    int i = enumerator.Current;

    Console.WriteLine(i);
}

(Lihat http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx untuk MSIL)

Kemudian di DotNet2.0 Generik datang dan Daftar. Foreach selalu merasa bagi saya untuk menjadi implementasi dari pola Vistor, (lihat Pola Desain oleh Gamma, Helm, Johnson, Vlissides).

Sekarang tentu saja dalam 3,5 kita bisa menggunakan Lambda untuk efek yang sama, misalnya coba http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- saya/

pengguna18784
sumber
1
Saya percaya kode yang dihasilkan untuk "foreach" harus memiliki blok coba-akhirnya. Karena IEnumerator dari T mewarisi dari antarmuka IDisposable. Jadi, dalam blok akhirnya yang dihasilkan, IEnumerator dari instance T harus dibuang.
Morgan Cheng
2

Saya ingin memperluas jawaban Aku .

Jika Anda ingin memanggil metode dengan tujuan semata-mata untuk efek samping tanpa mengulangi seluruh enumerable terlebih dahulu, Anda dapat menggunakan ini:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
    foreach (var x in xs) {
        f(x); yield return x;
    }
}
fredefox
sumber
1
Ini sama denganSelect(x => { f(x); return x;})
Jim Balter
0

Belum ada yang menunjukkan bahwa ForEach <T> menghasilkan kompilasi jenis waktu memeriksa di mana kata kunci foreach diperiksa runtime.

Setelah melakukan beberapa refactoring di mana kedua metode digunakan dalam kode, saya mendukung. ForEach, karena saya harus memburu kegagalan pengujian / kegagalan runtime untuk menemukan masalah pendahuluan.

Tupai
sumber
5
Benarkah ini masalahnya? Saya gagal melihat bagaimana foreachkompilasi waktu pengecekan kurang. Tentu, jika koleksi Anda adalah IEnumerable non-generik, variabel loop akan menjadi object, tetapi hal yang sama akan berlaku untuk ForEachmetode ekstensi hipotetis .
Richard Poole
1
Ini disebutkan dalam jawaban yang diterima. Namun, itu hanya salah (dan Richard Poole di atas sudah benar).
Jim Balter