Tại sao không có phương thức mở rộng ForEach trên IEnumerable?


389

Lấy cảm hứng từ một câu hỏi khác hỏi về Zipchức năng còn thiếu :

Tại sao không có ForEachphương thức mở rộng trong Enumerablelớp? Hay bất cứ nơi nào? Lớp duy nhất có được một ForEachphương thức là List<>. Có một lý do tại sao nó thiếu (hiệu suất)?


Có lẽ như được gợi ý bởi một bài đăng trước đó, trong đó foreach được hỗ trợ như một từ khóa ngôn ngữ trong C # và VB.NET (và nhiều người khác) Hầu hết mọi người (bao gồm cả tôi) chỉ đơn giản là tự viết một khi cần thiết và phù hợp.
chrisb

2
Câu hỏi liên quan ở đây với liên kết đến 'câu trả lời chính thức' stackoverflow.com/questions/858978/
Kẻ


Tôi nghĩ một điểm rất quan trọng là, vòng lặp foreach dễ phát hiện hơn. Nếu bạn sử dụng .orEach (...) rất dễ bỏ lỡ cái này, khi bạn xem qua mã. Điều này trở nên quan trọng, khi bạn có vấn đề về hiệu suất. Tất nhiên .ToList () và .ToArray () có cùng một vấn đề nhưng chúng được sử dụng hơi khác nhau. Một lý do khác có thể là nó phổ biến hơn trong một .orEach để sửa đổi danh sách nguồn (loại bỏ / thêm các phần tử) để nó không còn là truy vấn "thuần túy" nữa.
Daniel Bişar

Khi gỡ lỗi mã parallelised, tôi thường cố gắng trao đổi Parallel.ForEachcho Enumerable.ForEachduy nhất để tái khám phá sau này không tồn tại. C # đã bỏ lỡ một mẹo để làm cho mọi thứ dễ dàng ở đây.
Đại tá Panic

Câu trả lời:


198

Đã có một tuyên bố foreach bao gồm trong ngôn ngữ làm công việc hầu hết thời gian.

Tôi ghét phải xem những điều sau đây:

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

Thay vì:

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

Cái sau rõ ràng hơn và dễ đọc hơn trong hầu hết các tình huống , mặc dù có thể lâu hơn một chút để gõ.

Tuy nhiên, tôi phải thừa nhận tôi đã thay đổi lập trường của mình về vấn đề đó; một phương thức mở rộng ForEach () thực sự sẽ hữu ích trong một số trường hợp.

Dưới đây là những khác biệt chính giữa tuyên bố và phương thức:

  • Kiểm tra kiểu: foreach được thực hiện trong thời gian chạy, ForEach () đang ở thời gian biên dịch (Big Plus!)
  • Cú pháp để gọi một đại biểu thực sự đơn giản hơn nhiều: object.ForEach (DoS Something);
  • ForEach () có thể bị xiềng xích: mặc dù tính xấu / tính hữu dụng của tính năng này được mở để thảo luận.

Đó là tất cả những điểm tuyệt vời được thực hiện bởi nhiều người ở đây và tôi có thể thấy tại sao mọi người thiếu chức năng. Tôi sẽ không nhớ Microsoft thêm một phương thức ForEach tiêu chuẩn trong lần lặp khung tiếp theo.


39
Các cuộc thảo luận ở đây đưa ra câu trả lời: forum.microsoft.com/MSDN/, Về cơ bản, quyết định được đưa ra là giữ cho các phương thức mở rộng hoạt động "thuần túy". Một ForEach sẽ khuyến khích các tác dụng phụ khi sử dụng các phương thức mở rộng, không phải là mục đích.
mancaus

17
'foreach' có vẻ rõ ràng hơn nếu bạn có nền C, nhưng lặp lại bên trong nói rõ hơn ý định của bạn. Điều đó có nghĩa là bạn có thể làm những việc như 'Sequ.AsParallel (). ForEach (...)'.
Jay Bazuzi

10
Tôi không chắc quan điểm của bạn về kiểm tra loại là chính xác. foreachlà loại được kiểm tra tại thời điểm biên dịch nếu liệt kê được tham số hóa. Nó chỉ thiếu kiểm tra kiểu thời gian biên dịch cho các bộ sưu tập không chung chung, như một ForEachphương pháp mở rộng giả thuyết .
Richard Poole

49
Phương pháp mở rộng sẽ thực sự hữu ích trong chuỗi LINQ với ký hiệu dấu chấm, như : col.Where(...).OrderBy(...).ForEach(...). Cú pháp đơn giản hơn nhiều, không gây ô nhiễm màn hình với các biến cục bộ dự phòng hoặc dấu ngoặc dài trong foreach ().
Jacek Gorgoń

22
"Tôi ghét phải xem những điều sau" - Câu hỏi không phải là về sở thích cá nhân của bạn và bạn đã làm cho mã trở nên đáng ghét hơn bằng cách thêm các nguồn cấp dữ liệu không liên quan và một cặp niềng răng không liên quan. Không đáng ghét lắm đâu list.ForEach(item => item.DoSomething()). Và ai nói đó là một danh sách? Trường hợp sử dụng rõ ràng là ở cuối chuỗi; với câu lệnh foreach, bạn sẽ phải đặt toàn bộ chuỗi sau invà thao tác được thực hiện bên trong khối, điều này ít tự nhiên hoặc nhất quán hơn nhiều.
Jim Balter

73

Phương pháp ForEach đã được thêm vào trước LINQ. Nếu bạn thêm tiện ích mở rộng ForEach, nó sẽ không bao giờ được gọi cho các phiên bản Danh sách vì các ràng buộc về phương thức tiện ích mở rộng. Tôi nghĩ lý do nó không được thêm vào là không can thiệp vào cái hiện có.

Tuy nhiên, nếu bạn thực sự bỏ lỡ chức năng nhỏ xinh này, bạn có thể tung ra phiên bản của riêng mình

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

11
Các phương pháp mở rộng cho phép điều này. Nếu đã có một triển khai thực hiện của một tên phương thức đã cho thì phương thức đó được sử dụng thay cho phương thức mở rộng. Vì vậy, bạn có thể triển khai phương thức mở rộng ForEach và không bị xung đột với phương thức Danh sách <>. ForEach.
Cameron MacFarland

3
Cameron, tin tôi đi Tôi biết các phương thức mở rộng hoạt động như thế nào :) Danh sách kế thừa IEnumerable nên nó sẽ không phải là điều hiển nhiên.
aku

9
Từ Hướng dẫn lập trình phương thức mở rộng ( msdn.microsoft.com/en-us/l Library / bb383977.aspx ): Một phương thức mở rộng có cùng tên và chữ ký như một phương thức giao diện hoặc lớp sẽ không bao giờ được gọi. Có vẻ khá rõ ràng với tôi.
Cameron MacFarland

3
Tôi đang nói điều gì đó khác nhau? Tôi nghĩ rằng nó không tốt khi nhiều lớp có phương thức ForEach nhưng một số trong số chúng có thể hoạt động khác nhau.
aku

5
Một điều thú vị cần lưu ý về Danh sách <>. ForEach và Array.ForEach là họ không thực sự sử dụng cấu trúc foreach bên trong. Cả hai đều sử dụng một vòng lặp đơn giản cho vòng lặp. Có lẽ đó là một điều hiệu suất ( diditwith.net/2006/10/05/PerformanceOfForeachVsListForEach.aspx ). Nhưng do đó, cả Array và List đều thực hiện các phương thức ForEach, điều đáng ngạc nhiên là ít nhất họ đã không thực hiện một phương thức mở rộng cho IList <>, nếu không phải cho IEnumerable <>.
Matt Dotson

53

Bạn có thể viết phương thức mở rộng này:

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

Ưu

Cho phép xích:

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

Nhược điểm

Nó sẽ không thực sự làm bất cứ điều gì cho đến khi bạn làm một cái gì đó để buộc lặp đi lặp lại. Vì lý do đó, nó không nên được gọi .ForEach(). Bạn có thể viết .ToList()ở cuối hoặc bạn cũng có thể viết phương thức mở rộng này:

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

    return source;
}

Điều này có thể quá quan trọng khi rời khỏi thư viện C # vận chuyển; những người đọc không quen thuộc với các phương thức mở rộng của bạn sẽ không biết phải làm gì với mã của bạn.


1
Chức năng đầu tiên của bạn có thể được sử dụng theo phương pháp 'nhận ra' nếu được viết như sau:{foreach (var e in source) {action(e);} return source;}
jjnguy

3
Bạn không thể sử dụng Chọn thay vì phương thức Áp dụng của bạn? Dường như với tôi, việc áp dụng một hành động cho từng yếu tố và mang lại nó là chính xác những gì Chọn làm. Ví dụ:numbers.Select(n => n*2);
rasmusvhansen

1
Tôi nghĩ rằng Áp dụng dễ đọc hơn sử dụng Chọn cho mục đích này. Khi đọc một câu lệnh Chọn, tôi đọc nó là "lấy thứ gì đó từ bên trong" trong đó Áp dụng là "thực hiện một số công việc".
Bác sĩ Rob Lang

2
@rasmusvhansen Trên thực tế, Select()nói áp dụng một hàm cho từng phần tử và trả về giá trị của hàm trong đó Apply()nói áp dụng một Actioncho mỗi phần tử và trả về phần tử ban đầu .
NetMage

1
Điều này tương đương vớiSelect(e => { action(e); return e; })
Jim Balter

35

Các cuộc thảo luận ở đây đưa ra câu trả lời:

Trên thực tế, các cuộc thảo luận cụ thể mà tôi đã chứng kiến ​​đã thực sự xoay quanh độ tinh khiết của chức năng. Trong một biểu thức, thường có những giả định được đưa ra về việc không có tác dụng phụ. Có ForEach đặc biệt mời các tác dụng phụ thay vì chỉ đưa ra chúng. - Keith Farmer (Đối tác)

Về cơ bản, quyết định đã được đưa ra để giữ cho các phương thức mở rộng hoạt động "thuần túy". Một ForEach sẽ khuyến khích các tác dụng phụ khi sử dụng các phương thức mở rộng Vô số, không phải là mục đích.


6
Xem thêm blog.msdn.com/b/ericlippert/archive/2009/05/18/ . Làm như vậy vi phạm các nguyên tắc lập trình chức năng mà tất cả các toán tử trình tự khác dựa trên. Rõ ràng mục đích duy nhất của một cuộc gọi đến phương pháp này là gây ra tác dụng phụ
Michael Freidgeim

7
Lý do đó là hoàn toàn không có thật. Các trường hợp sử dụng là tác dụng phụ là cần thiết ... mà không ForEach, bạn phải viết một vòng lặp foreach. Sự tồn tại của ForEach không khuyến khích tác dụng phụ và sự vắng mặt của nó không làm giảm chúng.
Jim Balter

Cho rằng việc viết phương thức mở rộng đơn giản như thế nào, thực sự không có lý do gì mà nó không phải là tiêu chuẩn ngôn ngữ.
Thuyền trưởng Prinny

17

Mặc dù tôi đồng ý rằng tốt hơn là sử dụng foreachcấu trúc tích hợp trong hầu hết các trường hợp, tôi thấy việc sử dụng biến thể này trên tiện ích mở rộng ForEach <> sẽ đẹp hơn một chút so với việc phải tự mình quản lý chỉ mục 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;
}
Thí dụ
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));

Sẽ cung cấp cho bạn:

Person #0 is Moe
Person #1 is Curly
Person #2 is Larry

Mở rộng tuyệt vời, nhưng bạn có thể thêm một số tài liệu? Mục đích sử dụng của giá trị trả về là gì? Tại sao chỉ số var thay vì cụ thể là một int? Sử dụng mẫu?
Đánh dấu T

Mark: Giá trị trả về là số phần tử trong danh sách. Index được gõ cụ thể như một int, nhờ gõ ngầm.
Chris Zwiryk

@Chris, dựa trên mã của bạn ở trên, giá trị trả về là chỉ mục dựa trên số không của giá trị cuối cùng trong danh sách, không phải số lượng phần tử trong danh sách.
Eric Smith

@Chris, không, không phải: chỉ số được tăng sau khi giá trị được lấy (gia tăng hậu tố), vì vậy sau khi phần tử đầu tiên được xử lý, chỉ mục sẽ là 1, v.v. Vì vậy, giá trị trả về thực sự là số phần tử.
MartinStettner

1
@MartinStettner bình luận từ Eric Smith vào ngày 5/16 là từ phiên bản trước. Ông đã sửa mã vào ngày 18/5 để trả về giá trị đúng.
Michael Blackburn

16

Một cách giải quyết là viết .ToList().ForEach(x => ...).

ưu

Dễ hiểu - người đọc chỉ cần biết tàu nào có C #, không phải bất kỳ phương thức mở rộng bổ sung nào.

Tiếng ồn cú pháp rất nhẹ (chỉ thêm một chút mã ngoại lai).

Không thường tốn thêm bộ nhớ, vì .ForEach()dù sao người bản địa cũng sẽ phải nhận ra toàn bộ bộ sưu tập.

khuyết điểm

Thứ tự hoạt động không lý tưởng. Tôi muốn nhận ra một yếu tố, sau đó hành động theo nó, sau đó lặp lại. Mã này nhận ra tất cả các yếu tố trước, sau đó hành động theo từng chuỗi.

Nếu nhận ra danh sách ném một ngoại lệ, bạn không bao giờ phải hành động trên một yếu tố duy nhất.

Nếu phép liệt kê là vô hạn (như số tự nhiên), bạn sẽ không gặp may.


1
a) Đây không phải là một câu trả lời cho câu hỏi. b) Phản hồi này sẽ rõ ràng hơn nếu được đề cập trước rằng, trong khi không có Enumerable.ForEach<T>phương pháp, có một List<T>.ForEachphương pháp. c) "Thường không tốn thêm bộ nhớ, vì một người bản địa. Dù sao thì cũng sẽ phải nhận ra toàn bộ bộ sưu tập." - hoàn thành và hoàn toàn vô nghĩa. Đây là cách triển khai của Enumerable.ForEach <T> mà C # sẽ cung cấp nếu có:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Jim Balter

13

Tôi đã luôn tự hỏi rằng, đó là lý do tại sao tôi luôn mang theo thứ này bên mình:

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

Phương pháp mở rộng nhỏ đẹp.


2
col có thể là null ... bạn cũng có thể kiểm tra điều đó.
Amy B

Vâng tôi kiểm tra trong thư viện của tôi, tôi chỉ bỏ lỡ nó khi làm nó nhanh chóng ở đó.
Aaron Powell

Tôi thấy thú vị khi mọi người kiểm tra tham số hành động cho null, nhưng không bao giờ tham số nguồn / col.
Cameron MacFarland

2
Tôi thấy thú vị khi mọi người đều kiểm tra các tham số cho null, khi không kiểm tra kết quả trong một ngoại lệ ...
oɔɯǝɹ

Không có gì. Một ngoại lệ Null Ref sẽ không cho bạn biết tên của tham số bị lỗi. Và bạn cũng có thể kiểm tra trong vòng lặp để bạn có thể ném một Null Ref cụ thể nói rằng col [index #] là null vì lý do tương tự
Roger Willcocks

8

Vì vậy, đã có rất nhiều ý kiến ​​về thực tế rằng phương thức mở rộng ForEach không phù hợp vì nó không trả về giá trị như các phương thức mở rộng LINQ. Mặc dù đây là một tuyên bố thực tế, nó không hoàn toàn đúng.

Các phương thức mở rộng LINQ đều trả về một giá trị để chúng có thể được nối lại với nhau:

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

Tuy nhiên, chỉ vì LINQ được triển khai bằng các phương thức mở rộng không có nghĩa là các phương thức mở rộng phải được sử dụng theo cùng một cách và trả về một giá trị. Viết một phương thức mở rộng để hiển thị chức năng phổ biến không trả về giá trị là một cách sử dụng hoàn toàn hợp lệ.

Lập luận cụ thể về ForEach là, dựa trên các ràng buộc đối với các phương thức mở rộng (cụ thể là phương thức mở rộng sẽ không bao giờ ghi đè một phương thức được kế thừa có cùng chữ ký ), có thể có tình huống phương thức tiện ích mở rộng tùy chỉnh có sẵn trên tất cả các lớp. Vô số <T> ngoại trừ Danh sách <T>. Điều này có thể gây nhầm lẫn khi các phương thức bắt đầu hành xử khác nhau tùy thuộc vào việc phương thức mở rộng hoặc phương thức kế thừa đang được gọi.


7

Bạn có thể sử dụng (có thể đánh giá được, nhưng được đánh giá một cách lười biếng) Select, trước tiên thực hiện thao tác của bạn và sau đó trả lại danh tính (hoặc một cái gì đó khác nếu bạn muốn)

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

Bạn sẽ cần đảm bảo rằng nó vẫn được đánh giá, bằng Count()(hoạt động rẻ nhất để liệt kê afaik) hoặc hoạt động khác mà bạn cần dù sao đi nữa.

Tôi rất thích nhìn thấy nó được đưa vào thư viện tiêu chuẩn:

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

Các mã trên sau đó trở thành people.WithLazySideEffect(p => Console.WriteLine(p))tương đương hiệu quả với foreach, nhưng lười biếng và có thể xâu chuỗi.


Thật tuyệt vời, nó không bao giờ nhấp vào rằng một lựa chọn trả lại sẽ hoạt động như thế này. Một cách sử dụng tốt cú pháp phương thức, vì tôi không nghĩ bạn có thể làm điều này với cú pháp biểu thức (do đó tôi chưa từng nghĩ về nó như thế này trước đây).
Alex KeySmith

@AlexKeySmith Bạn có thể làm điều đó với cú pháp biểu thức nếu C # có toán tử trình tự, giống như tổ tiên của nó. Nhưng toàn bộ quan điểm của ForEach là nó thực hiện một hành động với loại khoảng trống và bạn không thể sử dụng loại khoảng trống trong biểu thức trong C #.
Jim Balter

Imho, bây giờ Selectkhông làm nữa những gì nó phải làm. Đó chắc chắn là một giải pháp cho những gì OP yêu cầu - nhưng về khả năng đọc chủ đề: không ai có thể ngờ rằng lựa chọn đó thực thi một chức năng.
Matthias Burger

@MatthiasBurger Nếu Selectkhông làm những gì nó phải làm thì đó là vấn đề với việc triển khai Select, không phải với mã gọi Select, không ảnh hưởng đến những gì Select.
Martijn


4

@ Bitcoin

Sức mạnh thực sự của phương thức mở rộng foreach liên quan đến khả năng sử dụng lại Action<>mà không cần thêm các phương thức không cần thiết vào mã của bạn. Giả sử bạn có 10 danh sách và bạn muốn thực hiện cùng một logic trên chúng và một hàm tương ứng không phù hợp với lớp của bạn và không được sử dụng lại. Thay vì có mười vòng lặp, hoặc một hàm chung rõ ràng là một trình trợ giúp không thuộc về, bạn có thể giữ tất cả logic của mình ở một nơi ( Action<>vì vậy, hàng chục dòng được thay thế bằng

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

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

Vân vân...

Logic ở một nơi và bạn đã không làm ô nhiễm lớp học của mình.


foreach (var p in List1.Concat (List2) .Concat (List3)) {... làm công cụ ...}
Cameron MacFarland

Nếu nó đơn giản như vậy f(p), thì hãy bỏ qua { }cho tốc ký : for (var p in List1.Concat(List2).Concat(List2)) f(p);.
spoulson

3

Hầu hết các phương pháp mở rộng LINQ trả về kết quả. ForEach không phù hợp với mẫu này vì nó không trả về gì cả.


2
ForEach sử dụng Hành động <T>, một hình thức đại biểu khác
Aaron Powell

2
Ngoại trừ quyền của leppie, tất cả các phương thức mở rộng Có thể trả về một giá trị, vì vậy ForEach không phù hợp với mẫu. Các đại biểu không tham gia vào việc này.
Cameron MacFarland

Chỉ cần một phương thức mở rộng không phải trả về kết quả, nó phục vụ mục đích thêm một cách để gọi một hoạt động thường được sử dụng
Aaron Powell

1
Thật vậy, Slace. Vậy nếu nó không trả về giá trị thì sao?
TraumaPony

1
@Martijn tức làpublic 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

Nếu bạn có F # (sẽ có trong phiên bản .NET tiếp theo), bạn có thể sử dụng

Seq.iter doSomething myIEnumerable


9
Vì vậy, Microsoft đã chọn không cung cấp phương thức ForEach cho IEnumerable trong C # và VB, vì nó sẽ không hoàn toàn là chức năng; nhưng họ DID cung cấp nó (tên iter) bằng ngôn ngữ chức năng hơn của họ, F #. Logic của họ ám chỉ tôi.
Scott Hutchinson

3

Một phần là do các nhà thiết kế ngôn ngữ không đồng ý với nó từ góc độ triết học.

  • Không có (và thử nghiệm ...) một tính năng sẽ ít hoạt động hơn so với việc có một tính năng.
  • Nó không thực sự ngắn hơn (có một số trường hợp chức năng vượt qua, nhưng đó không phải là sử dụng chính).
  • Mục đích của nó là có tác dụng phụ, đó không phải là vấn đề của linq.
  • Tại sao có một cách khác để làm điều tương tự như một tính năng chúng ta đã có? (từ khóa foreach)

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


2

Bạn có thể sử dụng chọn khi bạn muốn trả lại một cái gì đó. Nếu bạn không, bạn có thể sử dụng ToList trước, vì có lẽ bạn không muốn sửa đổi bất cứ điều gì trong bộ sưu tập.


a) Đây không phải là một câu trả lời cho câu hỏi. b) ToList yêu cầu thêm thời gian và bộ nhớ.
Jim Balter

2

Tôi đã viết một bài đăng trên blog về nó: http://bloss.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx

Bạn có thể bỏ phiếu tại đây nếu bạn muốn xem phương thức này trong .NET 4.0: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093


điều này đã bao giờ được thực hiện? Tôi tưởng tượng là không, nhưng có URL nào khác thay thế cho vé đó không? Kết nối MS đã bị ngừng
knocte

Điều này đã không được thực hiện. Xem xét việc gửi một vấn đề mới trên github.com/dotnet/r
nb /issues


2

Là tôi hay là Danh sách <T>. Phần lớn đã bị Linq làm cho lỗi thời. Ban đầu có

foreach(X x in Y) 

trong đó Y đơn giản phải là IEnumerable (Pre 2.0) và triển khai GetEnumerator (). Nếu bạn nhìn vào MSIL được tạo ra, bạn có thể thấy rằng nó giống hệt như

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

    Console.WriteLine(i);
}

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

Sau đó, trong DotNet2.0 Generics xuất hiện và Danh sách. Foreach luôn cảm thấy với tôi là một triển khai của mẫu Vistor, (xem Mẫu thiết kế của Gamma, Helm, Johnson, Vlissides).

Bây giờ tất nhiên trong 3,5, thay vào đó, chúng ta có thể sử dụng Lambda để có hiệu quả tương tự, ví dụ: thử http://dotnet-developments.bloss.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- của tôi /


1
Tôi tin rằng mã được tạo cho "foreach" nên có một khối thử cuối cùng. Bởi vì IEnumerator của T kế thừa từ giao diện IDis Dùng. Vì vậy, trong khối cuối cùng được tạo, IEnumerator của T dụ nên được xử lý.
Morgan Cheng

2

Tôi muốn mở rộng câu trả lời của Aku .

Nếu bạn muốn gọi một phương thức cho mục đích duy nhất là hiệu ứng phụ của nó mà không lặp lại toàn bộ số đầu tiên, bạn có thể sử dụng phương thức này:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) {
    foreach (var x in xs) {
        f(x); yield return x;
    }
}

1
Điều này giống nhưSelect(x => { f(x); return x;})
Jim Balter

0

Không ai đã chỉ ra rằng ForEach <T> dẫn đến việc kiểm tra kiểu thời gian biên dịch trong đó từ khóa foreach được kiểm tra thời gian chạy.

Đã thực hiện một số phép tái cấu trúc trong đó cả hai phương thức được sử dụng trong mã, tôi ủng hộ .orEach, vì tôi phải tìm kiếm các lỗi thử nghiệm / lỗi thời gian chạy để tìm các vấn đề về foreach.


5
Đây đúng là tình trạng đó phải không? Tôi không thấy làm thế nào foreachcó ít thời gian biên dịch kiểm tra. Chắc chắn, nếu bộ sưu tập của bạn là một IEnumerable không chung chung, biến vòng lặp sẽ là một object, nhưng điều tương tự sẽ đúng với ForEachphương pháp mở rộng giả thuyết .
Richard Poole

1
Điều này được đề cập trong câu trả lời được chấp nhận. Tuy nhiên, nó chỉ đơn giản là sai (và Richard Poole ở trên là chính xác).
Jim Balter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.