LINQで次のようなことをしたいのですが、どうすればよいかわかりません。
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
本当の構文は何ですか?
IEnumerable
にはForEach拡張はありません。 List<T>
専用です。だからあなたはできる
items.ToList().ForEach(i => i.DoStuff());
あるいは、独自のForEach拡張メソッドを書きます。
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach(T item in enumeration)
{
action(item);
}
}
Fredrikがこの修正を提供しましたが、なぜこれが最初からフレームワークに含まれていないのかを検討する価値があります。私は、LINQクエリ演算子は副作用のない、世界を見るための合理的に機能的な方法に適合するべきであると考えています。明らかにForEachは正反対です - 純粋に 副作用ベースの構文。
これは悪いことではありません。決定の背後にある哲学的な理由について考えるだけです。
Update 7/17/2012:C#5.0から、下記のforeach
の振る舞いが変更され、 " 入れ子になったラムダ式でforeach
反復変数を使用しても、予期しない結果が生じることはなくなりました。 "この答えはC#≧5.0には適用されません。
@John Skeetとforeachキーワードを好む人全員。
C#5.0より前の "foreach"の問題は、他の言語での "for forhension"と同等の動作と、それがどのように動作すると予想されるかとが矛盾することです。他の人が読みやすさに関する意見を述べているからといってここで述べています。 「 変更されたクロージャへのアクセス 」および「 ループ変数を閉じることは有害と見なされる 」に関する質問をすべて参照してください。これは、「foreach」がC#で実装されているため、「有害」にすぎません。
次の例では、@ 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);
}
}
}
あまりにも工夫を凝らした例に対する謝罪。 Observableを使っているのは、このようなことをするために完全には取り込まれていないからです。明らかに、このオブザーバブルを作成するためのより良い方法があります、私はポイントを実証しようとしているだけです。通常、オブザーバブルをサブスクライブしているコードは非同期的に、場合によっては別のスレッドで実行されます。 「foreach」を使用すると、これは非常に奇妙で潜在的に非決定的な結果を生み出す可能性があります。
"ForEach"拡張メソッドを使用した次のテストに合格します。
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
以下はエラーで失敗します。
予想される値:<0、1、2、3、4、5、6、7、8、9>と同等。ただし<9、9、9、9、9、9、9、9、9、 9>
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
IEnumerable<T>
で利用可能なFirstOrDefault()
拡張子を使うことができます。述語からfalse
を返すことで、それは各要素に対して実行されますが、実際には一致が見つからないことを気にすることはありません。これはToList()
のオーバーヘッドを避けます。
IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
私はFredrikの方法を取り、戻り型を修正しました。
このように、このメソッドは他のLINQメソッドと同様に 遅延実行 をサポートします。
EDIT:これが明確でなかった場合、このメソッドの使用方法はToList()で終わらなければなりません。そうでなければ、アクションは実行されません。
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (T item in enumeration)
{
action(item);
yield return item;
}
}
そして、これを確認するためのテストがあります。
[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};
var sb = new StringBuilder();
enumerable
.ForEach(c => sb.Append("1"))
.ForEach(c => sb.Append("2"))
.ToList();
Assert.That(sb.ToString(), Is.EqualTo("121212"));
}
最後にToList()を削除すると、StringBuilderに空の文字列が含まれているため、テストが失敗したことになります。これは、どのメソッドもForEachに列挙を強制しなかったためです。
LINQで次のようなことをしたいのですが、どうすればよいかわかりません。
他の人が指摘したように ここ そして海外ではLINQとIEnumerable
メソッドは副作用がないと期待されています。
あなたは本当にIEnumerableの各項目に「何かをしたい」のですか?それならforeach
が最良の選択です。ここで副作用が発生しても人々は驚かない。
foreach (var i in items) i.DoStuff();
しかし、私の経験では副作用は通常必要ありません。たいていの場合、Jon Skeet、Eric Lippert、またはMarc GravellによるStackOverflow.comの回答を伴って、単純なLINQクエリが発見されるのを待っているのです。
実際に単に値を集計(累積)しているだけの場合は、Aggregate
拡張メソッドを検討する必要があります。
items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));
おそらく、既存の値から新しいIEnumerable
を作成したいでしょう。
items.Select(x => Transform(x));
あるいは、ルックアップテーブルを作成したいかもしれません。
items.ToLookup(x, x => GetTheKey(x))
可能性のリスト(完全に意図されているわけではない)は延々と続く。
あなたが列挙ロールとして振る舞いたいのであれば、あなたはそれぞれのアイテムを生み出すべきです。
public static class EnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (var item in enumeration)
{
action(item);
yield return item;
}
}
}
Interactive Extensions to LINQ ( NuGet上 、 RxTeamsのプロファイル 参照)のMicrosoftによる実験的リリースがあります。 チャンネル9ビデオ はそれをよく説明しています。
そのドキュメントはXMLフォーマットでのみ提供されています。読みやすくするために、これを実行しました Sandcastleのドキュメント docsアーカイブを解凍し、 index.html を探します。
他の多くの優れたものの中で、それは期待されたForEach実装を提供します。それはあなたがこのようなコードを書くことを可能にします:
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };
numbers.ForEach(x => Console.WriteLine(x*x));
PLINQ(.Net 4.0以降で利用可能)によると、
IEnumerable<T>.AsParallel().ForAll()
iEnumerableで並列foreachループを実行します。
ForEachの目的は副作用を引き起こすことです。 IEnumerableはセットの遅延列挙のためのものです。
この概念上の違いは、あなたがそれを考えたときにはっきりと見えます。
SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));
これは、 "count"や "ToList()"、あるいはそれに対して何かをするまで実行されません。それは明らかに表現されたものではありません。
IEnumerable拡張機能を使用して、一連の反復を設定し、それぞれのソースと条件でコンテンツを定義します。 Expression Treeは強力で効率的ですが、その性質を理解することを学ぶべきです。そして怠惰な評価を無効にして少数の文字を節約するためにそれらのまわりでプログラミングするためだけではない。
多くの人がそれを述べました、しかし私はそれを書き留めなければなりませんでした。これは最も明瞭で読みやすいものではないでしょうか。
IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();
短くてシンプル(st)。
多くの答えがすでに指摘しているように、あなたは簡単にあなた自身でそのような拡張メソッドを追加することができます。しかし、そうしたくないのであれば、BCLにはこのようなことは何もありませんが、 Reactive Extension への参照がある場合は、System
name__名前空間にまだオプションがあります。 (あなたがそうでなければ、あなたは持っているべきです):
using System.Reactive.Linq;
items.ToObservable().Subscribe(i => i.DoStuff());
メソッド名は少し異なりますが、最終的な結果はまさにあなたが探しているものです。
今、我々はの選択肢があります...
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
parallelOptions.MaxDegreeOfParallelism = 1;
#endif
Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));
もちろん、これはまったく新しいスレッドワームの可能性を開きます。
ps(フォントについては申し訳ありません。システムが決定したものです)
ForEachはChainedでも構いません。アクションの後にパイルラインに戻すだけです。 流暢なまま
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();});
実装のEagerバージョン。
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 Lazyバージョンは、 this のようにyield returnを使用しています。
public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
foreach (var item in enu)
{
action(item);
yield return item;
}
}
Tozy()など、具体化されるレイジーバージョンNEEDs、それ以外の場合は何も起こりません。以下のToolmakerSteveからの素晴らしいコメントを参照してください。
IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
.ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();
私は自分のライブラリにForEach()とForEachLazy()の両方を入れています。
Jon Skeetに触発されて、私は彼の解決策を次のように拡張しました。
拡張方法:
public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
foreach (var item in source)
{
var target = keySelector(item);
applyBehavior(target);
}
}
クライアント:
var jobs = new List<Job>()
{
new Job { Id = "XAML Developer" },
new Job { Id = "Assassin" },
new Job { Id = "Narco Trafficker" }
};
jobs.Execute(ApplyFilter, j => j.Id);
。 。 。
public void ApplyFilter(string filterId)
{
Debug.WriteLine(filterId);
}
私は尊重していますが、リンクエクステンションメソッドは副作用のないものにすべきだという考えには賛成できません(そうではないという理由だけでなく、どのデリゲートも副作用を実行できるのです)。
次の点を考慮してください。
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);
}
}
この例が示すものは、アクションを定義し変換する値をデコードするための大きなswitch構造を記述する必要なしに、一連の要素に副作用を持つ可能なアクションの1つを呼び出すことを可能にする一種の遅延バインディングです。対応するメソッドに入れてください。
MoreLinqにはIEnumerable<T>.ForEach
とその他多数の便利な拡張機能があります。おそらくForEach
のためだけに依存関係を取る価値はありませんが、そこにはたくさんの役に立つものがあります。
流暢さを保つために、そのようなトリックを使うことができます。
GetItems()
.Select(i => new Action(i.DoStuf)))
.Aggregate((a, b) => a + b)
.Invoke();
VB.NETのためにあなたは使うべきです:
listVariable.ForEach(Sub(i) i.Property = "Value")
さらに別のForEach
の例
public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
var workingAddresses = new List<AddressEntry>();
addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));
return workingAddresses;
}
この「機能的アプローチ」の抽象化は大きな時間をかけます。言語レベルでは副作用を防ぐものは何もありません。コンテナ内のすべての要素に対してラムダ/デリゲートを呼び出すことができる限り、 "ForEach"の動作が得られます。
例えば、srcDictionaryをdestDictionaryにマージする1つの方法(キーが既に存在する場合は上書き)
これはハックであり、いかなるプロダクションコードでも使うべきではありません。
var b = srcDictionary.Select(
x=>
{
destDictionary[x.Key] = x.Value;
return true;
}
).Count();