LINQ tương đương với foreach cho IEn Countable <T>


717

Tôi muốn thực hiện tương tự như sau trong LINQ, nhưng tôi không thể tìm ra cách:

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

Cú pháp thực sự là gì?


99
Hãy xem xét "foreach" so với "ForEach" của Eric Lippert. Nó cung cấp biện minh tốt cho việc không sử dụng / cung cấp a ForEach().

4
@pst: Tôi đã sử dụng một cách mù quáng phương pháp mở rộng này trong các dự án của mình. Cảm ơn bài viết đó.
Robin Maben


2
Mặc dù không gợi cảm lắm, nhưng điều này phù hợp trên một dòng mà không tạo bản sao qua ToList -foreach (var i in items) i.Dostuff();
James Westgate

1
Một số liên kết này đã chết! Bài báo đã được bình chọn rất nhiều!
Warren

Câu trả lời:


863

Không có phần mở rộng ForEach cho IEnumerable; chỉ cho List<T>. Vì vậy, bạn có thể làm

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

Hoặc, viết phương thức mở rộng ForEach của riêng bạn:

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

213
Hãy cẩn thận với ToList (), vì ToList () tạo một bản sao của chuỗi, điều này có thể gây ra các vấn đề về hiệu năng và bộ nhớ.
decasteljau

15
Có một vài lý do tại sao ".oreach ()" sẽ tốt hơn. 1. Đa luồng, có thể bằng PLINQ. 2. Ngoại lệ, bạn có thể muốn thu thập tất cả ngoại lệ trong vòng lặp và ném nó cùng một lúc; và chúng là mã tiếng ồn.
Dennis C

12
PLINQ sử dụng For ALL không phải ForEach, đó là lý do tại sao bạn sẽ không sử dụng ForEach.
dùng7116

9
Bạn nên trả về một IENumerable<T>trong phương thức mở rộng. Giống như: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
Lưu ý rằng trong trường hợp đầu tiên, nhà phát triển 1) tiết kiệm gần như không gõ và 2) phân bổ bộ nhớ không cần thiết để lưu trữ toàn bộ chuỗi dưới dạng danh sách trước khi vứt nó đi. Hãy suy nghĩ trước khi bạn LINQ
Mark Sowul

364

Fredrik đã cung cấp bản sửa lỗi, nhưng có thể đáng để xem xét tại sao điều này không nằm trong khuôn khổ để bắt đầu. Tôi tin rằng ý tưởng là các toán tử truy vấn LINQ không có tác dụng phụ, phù hợp với cách nhìn hợp lý về thế giới. Rõ ràng ForEach hoàn toàn ngược lại - một cấu trúc hoàn toàn dựa trên hiệu ứng phụ.

Điều đó không phải để nói rằng đây là một điều xấu để làm - chỉ nghĩ về những lý do triết học đằng sau quyết định.


44
Chưa hết, F # có Seq.iter
Stephen Swensen

6
Tuy nhiên, có một điều rất hữu ích là các hàm LINQ thường cung cấp foreach () không - hàm ẩn danh được thực hiện bởi các hàm Linq cũng có thể lấy một chỉ mục làm tham số, trong khi với foreach, bạn phải xây dựng lại cổ điển cho (int i .. ) xây dựng. Và các ngôn ngữ chức năng phải cho phép phép lặp có tác dụng phụ - nếu không bạn không bao giờ có thể thực hiện một iota công việc với chúng :) Tôi muốn chỉ ra rằng phương thức chức năng điển hình sẽ trả về số lượng không thay đổi ban đầu.
Walt W

3
@Walt: Tôi vẫn không chắc là mình đồng ý, một phần vì cách tiếp cận chức năng thực sự điển hình sẽ không sử dụng foreach như thế này ... nó sẽ sử dụng cách tiếp cận chức năng giống như LINQ, tạo ra một chuỗi mới.
Jon Skeet

12
@Stephen Swensen: có, F # có Seq.iter, nhưng F # cũng có cấu trúc dữ liệu bất biến. khi sử dụng Seq.iter trên những cái này, Seq ban đầu không được sửa đổi; một Seq mới với các yếu tố tác động phụ được trả lại.
Anders

7
@Anders - Seq.iterkhông trả lại bất cứ điều gì, hãy để một seq mới với các yếu tố tác dụng phụ. Nó thực sự chỉ là về tác dụng phụ. Bạn có thể nghĩ đến Seq.map, bởi vì Seq.iterchính xác là tương đương với một ForEachphương pháp mở rộng trên IEnumerabe<_>.
Joel Mueller

39

Cập nhật 17/07/2012: Rõ ràng là từ C # 5.0, hành vi foreachđược mô tả dưới đây đã được thay đổi và " việc sử dụng foreachbiến lặp trong biểu thức lambda lồng nhau không còn tạo ra kết quả bất ngờ. " Câu trả lời này không áp dụng cho C # 5.0 .

@ John Skeet và tất cả những người thích từ khóa foreach.

Vấn đề với "foreach" trong C # trước 5.0 , là nó không phù hợp với cách thức "hiểu" tương đương trong các ngôn ngữ khác và với cách tôi mong đợi nó hoạt động (ý kiến ​​cá nhân chỉ nêu ở đây vì những người khác đã đề cập ở đây ý kiến ​​về khả năng đọc). Xem tất cả các câu hỏi liên quan đến " Truy cập vào đóng cửa được sửa đổi " cũng như " Đóng trên biến vòng lặp được coi là có hại ". Điều này chỉ "có hại" vì cách "foreach" được thực hiện trong C #.

Lấy các ví dụ sau bằng cách sử dụng phương thức mở rộng tương đương về chức năng với câu trả lời của @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);
        }
    }
}

Lời xin lỗi cho ví dụ quá mức. Tôi chỉ sử dụng Observable vì nó không hoàn toàn xa để làm một cái gì đó như thế này. Rõ ràng có nhiều cách tốt hơn để tạo ra điều này có thể quan sát được, tôi chỉ đang cố gắng chứng minh một điểm. Thông thường, mã được đăng ký để có thể quan sát được thực thi không đồng bộ và có khả năng trong một luồng khác. Nếu sử dụng "foreach", điều này có thể tạo ra kết quả rất kỳ lạ và có khả năng không xác định.

Thử nghiệm sau bằng cách sử dụng phương thức mở rộng "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()));
}

Lỗi sau đây với lỗi:

Dự kiến: tương đương với <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Nhưng là: <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()));
}

Cảnh báo chia sẻ lại nói gì?
reggaeg Ức

1
@reggaeg Ức Tôi chưa sử dụng C # trong 7 hoặc 8 năm, kể từ trước khi C # 5.0 được phát hành, điều này đã thay đổi hành vi được mô tả ở đây. Tại thời điểm đó, nó đưa ra cảnh báo stackoverflow.com/questions/1688465/ trên . Tôi nghi ngờ rằng nó vẫn cảnh báo về điều này mặc dù.
drstevens

34

Bạn có thể sử dụng FirstOrDefault()phần mở rộng, có sẵn cho IEnumerable<T>. Bằng cách trở về falsetừ vị ngữ, nó sẽ được chạy cho từng phần tử nhưng sẽ không quan tâm rằng nó không thực sự tìm thấy kết quả khớp. Điều này sẽ tránh được ToList()chi phí.

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

11
Tôi cũng đồng ý. Nó có thể là một hack nhỏ thông minh, nhưng ngay từ cái nhìn đầu tiên, mã này không rõ ràng những gì nó đang làm, chắc chắn so với một vòng lặp foreach tiêu chuẩn.
Connell

26
Tôi đã phải hạ thấp vì trong khi về mặt kỹ thuật , con người tương lai của bạn và tất cả những người kế nhiệm của bạn sẽ ghét bạn mãi mãi vì thay vì foreach(Item i in GetItems()) { i.DoStuff();}bạn lấy thêm nhân vật và khiến nó trở nên vô cùng khó hiểu
Mark Sowul

14
Ok, nhưng hoàn toàn là một bản hack dễ đọc hơn:items.All(i => { i.DoStuff(); return true; }
nawfal

5
Tôi biết đây là một bài viết khá cũ, nhưng tôi cũng phải đánh giá thấp nó. Tôi đồng ý rằng đó là một mẹo thông minh, nhưng nó không thể duy trì được. Thoạt nhìn có vẻ như nó đang làm gì đó với mục đầu tiên trong danh sách. Điều này có thể dễ dàng gây ra nhiều lỗi hơn trong tương lai bởi các lập trình viên hiểu sai về cách thức hoạt động của nó và những gì nó được cho là đang làm.
Lee

4
Đây là chương trình hiệu ứng phụ và IMHO không khuyến khích.
Anish

21

Tôi lấy phương pháp của Fredrik và sửa đổi kiểu trả về.

Theo cách này, phương thức hỗ trợ thực thi hoãn lại như các phương thức LINQ khác.

EDIT: Nếu điều này không rõ ràng, bất kỳ việc sử dụng phương thức này phải kết thúc bằng ToList () hoặc bất kỳ cách nào khác để buộc phương thức hoạt động trên toàn bộ số đếm. Nếu không, hành động sẽ không được thực hiện!

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

Và đây là bài kiểm tra để giúp xem nó:

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

Nếu bạn loại bỏ ToList () cuối cùng, bạn sẽ thấy thử nghiệm thất bại do StringBuilder chứa một chuỗi rỗng. Điều này là do không có phương pháp nào buộc ForEach liệt kê.


Việc thực hiện thay thế của bạn ForEachlà thú vị, tuy nhiên nó không phù hợp với hành vi List.ForEach, chữ ký của nó public void ForEach(Action<T> action). Nó cũng không phù hợp với hành vi của Observable.ForEachphần mở rộng IObservable<T>, chữ ký của nó public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext). FWIW, ForEachtương đương trên các bộ sưu tập Scala, ngay cả những người lười biếng, cũng có loại trả về tương đương với khoảng trống trong C #.
drstevens

Đây là câu trả lời tốt nhất: thực hiện hoãn lại + api trôi chảy.
Alexander Danilov

3
Điều này hoạt động chính xác giống như gọi Selectvới ToList. Mục đích của ForEachviệc không phải gọi ToList. Nó sẽ thực hiện ngay lập tức.
t3chb0t

3
Điều gì sẽ là lợi ích của việc có "ForEach" này, mà việc thực thi bị hoãn lại, so với việc thực thi ngay lập tức? Trì hoãn (không thuận tiện trong cách sử dụng ForEach) sẽ không cải thiện thực tế là ForEach chỉ thực hiện công việc hữu ích nếu Hành động có tác dụng phụ [khiếu nại thông thường được đưa ra đối với nhà điều hành Linq như vậy]. Vì vậy, làm thế nào để thay đổi này giúp? Ngoài ra, "ToList ()" được thêm vào để buộc nó thực thi, không phục vụ mục đích hữu ích. Đó là một tai nạn đang chờ để xảy ra. Điểm của "ForEach" là tác dụng phụ của nó. Nếu bạn muốn một bảng liệt kê trả lại, CHỌN có ý nghĩa hơn.
ToolmakerSteve

20

Giữ tác dụng phụ của bạn ra khỏi IEnumerable của tôi

Tôi muốn thực hiện tương tự như sau trong LINQ, nhưng tôi không thể tìm ra cách:

Như những người khác đã chỉ ra ở đây và ở nước ngoài LINQ và IEnumerablecác phương pháp dự kiến ​​sẽ không có tác dụng phụ.

Bạn có thực sự muốn "làm một cái gì đó" cho từng mục trong IEnumerable không? Sau đó foreachlà sự lựa chọn tốt nhất. Mọi người không ngạc nhiên khi tác dụng phụ xảy ra ở đây.

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

Tôi cá là bạn không muốn có tác dụng phụ

Tuy nhiên, theo kinh nghiệm của tôi, tác dụng phụ thường không bắt buộc. Thường xuyên hơn không có một truy vấn LINQ đơn giản đang chờ được phát hiện kèm theo câu trả lời StackOverflow.com của Jon Skeet, Eric Lippert hoặc Marc Gravell giải thích cách làm những gì bạn muốn!

Vài ví dụ

Nếu bạn thực sự chỉ đang tổng hợp (tích lũy) một số giá trị thì bạn nên xem xét Aggregatephương pháp mở rộng.

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

Có lẽ bạn muốn tạo một cái mới IEnumerabletừ các giá trị hiện có.

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

Hoặc có thể bạn muốn tạo một bảng tra cứu:

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

Danh sách (chơi chữ không hoàn toàn dự định) các khả năng cứ lặp đi lặp lại.


7
À, vậy là Chọn là Bản đồ và Tổng hợp là Giảm.
Darrel Lee

17

Nếu bạn muốn đóng vai trò là bảng liệt kê, bạn nên nhường từng mục.

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

Đây không thực sự là một ForEach khi bạn trả lại vật phẩm, nó giống như một Tap.
EluciusFTW

10

Có một bản phát hành thử nghiệm của Microsoft về Phần mở rộng tương tác cho LINQ (cũng trên NuGet , xem hồ sơ của RxTeams để biết thêm liên kết). Video Kênh 9 giải thích điều đó tốt.

Tài liệu của nó chỉ được cung cấp ở định dạng XML. Tôi đã chạy tài liệu này trong Sandcastle để cho phép nó ở định dạng dễ đọc hơn. Giải nén kho lưu trữ tài liệu và tìm kiếm index.html .

Trong số nhiều tính năng khác, nó cung cấp triển khai ForEach dự kiến. Nó cho phép bạn viết mã như thế này:

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

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

2
Liên kết hoạt động với Tiện ích mở rộng tương tác: microsoft.com/doad/en/details.aspx?id=27203
Maciek wiszczowski

8

Theo PLINQ (có sẵn từ .Net 4.0), bạn có thể thực hiện

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

để thực hiện một vòng lặp foreach song song trên IEnumerable.


miễn là hành động của bạn là chủ đề an toàn, tất cả đều tốt. Nhưng điều gì sẽ xảy ra nếu bạn có một hành động không an toàn để thực hiện? nó có thể gây ra khá một tình trạng lộn xộn ...
shirbr510

2
Thật. Nó không phải là chủ đề an toàn, các chủ đề khác nhau sẽ được sử dụng từ một số nhóm chủ đề ... Và tôi cần xem lại câu trả lời của mình. Không có ForEach () khi tôi dùng thử ngay bây giờ .. Phải là mã của tôi có chứa Phần mở rộng với ForEach khi tôi suy nghĩ về điều này.
Wolf5 17/03/2016

6

Mục đích của ForEach là gây ra tác dụng phụ. IEnumerable dành cho liệt kê lười biếng của một bộ.

Sự khác biệt về khái niệm này là khá rõ ràng khi bạn xem xét nó.

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

Điều này sẽ không thực thi cho đến khi bạn thực hiện "đếm" hoặc "ToList ()" hoặc một cái gì đó trên đó. Nó rõ ràng không phải là những gì được thể hiện.

Bạn nên sử dụng các tiện ích mở rộng IEnumerable để thiết lập các chuỗi lặp, xác định nội dung theo các nguồn và điều kiện tương ứng của chúng. Cây biểu hiện mạnh mẽ và hiệu quả, nhưng bạn nên học cách đánh giá cao bản chất của chúng. Và không chỉ để lập trình xung quanh họ để lưu một vài ký tự ghi đè lên đánh giá lười biếng.


5

Nhiều người đã đề cập đến nó, nhưng tôi đã phải viết nó ra. Đây không phải là rõ ràng nhất / dễ đọc nhất?

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

Ngắn gọn và đơn giản (st).


Bạn có thể thả toàn bộ dòng đầu tiên và sử dụng GetItems trực tiếp trong foreach
tymtam

3
Tất nhiên. Nó được viết như thế này cho rõ ràng. Vì vậy, rõ ràng GetItems()phương thức trả về.
Nenad

4
Bây giờ khi tôi đọc bình luận của tôi, tôi thấy có vẻ như tôi đang nói với bạn điều mà bạn không biết. Tôi không có ý đó. Ý tôi là đó foreach (var item in GetItems()) item.DoStuff();chỉ là một vẻ đẹp.
tymtam

4

Bây giờ chúng ta có tùy chọn ...

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

Tất nhiên, điều này mở ra một lon giun chỉ hoàn toàn mới.

ps (Xin lỗi về các phông chữ, đó là những gì hệ thống quyết định)


4

Như nhiều câu trả lời đã chỉ ra, bạn có thể dễ dàng thêm một phương thức mở rộng như vậy. Tuy nhiên, nếu bạn không muốn làm điều đó, mặc dù tôi không biết bất cứ điều gì như thế này trong BCL, vẫn có một tùy chọn trong Systemkhông gian tên, nếu bạn đã có tham chiếu đến Tiện ích mở rộng phản ứng (và nếu bạn không , bạn nên có):

using System.Reactive.Linq;

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

Mặc dù tên phương thức hơi khác một chút, nhưng kết quả cuối cùng chính xác là thứ bạn đang tìm kiếm.


Có vẻ như gói đó không được chấp nhận ngay bây giờ
reggaeg Ức

3

ForEach cũng có thể bị xích , chỉ cần đưa trở lại phi công sau khi hành động. vẫn thông thạo


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

Một phiên bản Eager thực hiện.

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
}

Chỉnh sửa: một phiên bản Lazy đang sử dụng lợi nhuận, như thế này .

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

Phiên bản Lazy CẦN phải được vật chất hóa, ví dụ như ToList (), nếu không, không có gì xảy ra. xem bên dưới những bình luận tuyệt vời từ ToolmakerSteve.

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

Tôi giữ cả ForEach () và ForEachLazy () trong thư viện của mình.


2
Bạn đã thử nghiệm điều này? Có một số bối cảnh mà phương pháp này hành xử theo trực giác. Tốt hơn làforeach(T item in enu) {action(item); yield return item;}
Taemyr

2
Điều này sẽ dẫn đến nhiều bảng liệt kê
regisbsb

Hấp dẫn. Suy ngẫm khi tôi muốn làm như trên, cho một chuỗi 3 hành động, chứ không phải là foreach (T item in enu) { action1(item); action2(item); action3(item); }? Một vòng lặp duy nhất, rõ ràng ,. Tôi đoán nếu điều quan trọng là phải thực hiện tất cả các hành động1 TRƯỚC KHI bắt đầu thực hiện hành động2.
ToolmakerSteve

@ Rm558 - cách tiếp cận của bạn bằng cách sử dụng "lợi nhuận". Pro: chỉ liệt kê chuỗi gốc một lần, do đó hoạt động chính xác với phạm vi liệt kê rộng hơn. Con: nếu bạn quên ".ToArray ()" ở cuối, không có gì xảy ra. Mặc dù sự thất bại sẽ là rõ ràng, không bỏ qua lâu, vì vậy không phải là một chi phí lớn. Không "cảm thấy sạch sẽ" với tôi, nhưng là sự đánh đổi đúng đắn, nếu một người đi xuống con đường này (một nhà điều hành ForEach).
ToolmakerSteve

1
Mã này không an toàn từ quan điểm giao dịch. Điều gì nếu nó thất bại trong ProcessShipping()? Bạn gửi email cho người mua, tính phí thẻ tín dụng của anh ta, nhưng không bao giờ gửi cho anh ta công cụ của mình? Chắc chắn yêu cầu vấn đề.
zmechanic

2

Sự tiếp cận "chức năng" này bị rò rỉ thời gian lớn. Không có gì ở cấp độ ngôn ngữ ngăn ngừa tác dụng phụ. Miễn là bạn có thể làm cho nó gọi lambda / ủy nhiệm của bạn cho mọi phần tử trong vùng chứa - bạn sẽ nhận được hành vi "ForEach".

Ở đây, ví dụ, một cách để hợp nhất srcDipedia vào DestDixi (nếu khóa đã tồn tại - ghi đè)

Đây là một hack và không nên được sử dụng trong bất kỳ mã sản xuất nào.

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

6
Nếu bạn phải thêm từ chối trách nhiệm đó, có lẽ bạn không nên đăng nó. Chỉ là ý kiến ​​của tôi.
Andrew Grothe

2
Làm thế quái nào tốt hơn foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }thế này? Nếu không có trong mã sản xuất ở đâu và tại sao bạn nên sử dụng cái này?
Mark Sowul

3
Điều này chỉ để cho thấy nó là có thể về nguyên tắc. Nó cũng xảy ra để làm những gì OP yêu cầu, và tôi chỉ rõ rằng nó không nên được sử dụng trong sản xuất, vẫn tò mò và có giá trị giáo dục.
Zar Shardan

2

Lấy cảm hứng từ Jon Skeet, tôi đã mở rộng giải pháp của mình bằng cách sau:

Phương pháp mở rộng:

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

Khách hàng:

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

Tôi không đồng ý với quan điểm rằng các phương pháp mở rộng liên kết sẽ không có tác dụng phụ (không chỉ vì chúng không có, bất kỳ đại biểu nào cũng có thể thực hiện các tác dụng phụ).

Hãy xem xét những điều sau đây:

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

Những gì ví dụ cho thấy thực sự chỉ là một loại ràng buộc muộn cho phép người ta gọi một trong nhiều hành động có thể có tác dụng phụ trên chuỗi các phần tử, mà không phải viết một cấu trúc chuyển đổi lớn để giải mã giá trị xác định hành động và dịch nó vào phương thức tương ứng của nó.


9
Có một sự khác biệt giữa việc một phương thức mở rộng CÓ THỂ có tác dụng phụ hay không và liệu nó có NÊN không. Bạn chỉ chỉ ra rằng nó có thể có, trong khi có thể có những lý do rất chính đáng để đề xuất rằng họ không nên có tác dụng phụ. Trong lập trình chức năng, tất cả các chức năng đều không có tác dụng phụ và khi sử dụng các cấu trúc lập trình chức năng, bạn có thể muốn giả sử rằng chúng là.
Dave Van den Eynde

1

Để thông thạo người ta có thể sử dụng một mẹo như vậy:

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


-1

Một ForEachví dụ khác

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
Thật là một sự lãng phí của bộ nhớ. Điều này gọi ToList để thêm vào danh sách. Tại sao điều này không trả về địa chỉ. Chọn (a => MapToDomain (a))?
Mark Sowul
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.