LINQ で次のようなことをしたいのですが、方法がわかりません。
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
本当の構文は何ですか?
LINQ で次のようなことをしたいのですが、方法がわかりません。
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
本当の構文は何ですか?
の ForEach 拡張機能はありませんIEnumerable
。のみ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 は正反対です。純粋に副作用ベースの構造です。
これが悪いことだと言っているわけではありません。決定の背後にある哲学的な理由について考えてみてください。
2012 年 7 月 17 日更新: どうやら C# 5.0 の時点で、foreach
以下に説明する動作が変更され、「ネストされたラムダ式で反復変数を使用しても、foreach
予期しない結果が生成されなくなりました。」この回答は、C# ≥ 5.0 には適用されません。 .
@John Skeet と foreach キーワードを好むすべての人。
5.0 より前の C# での「foreach」の問題は、他の言語で同等の「for Comprehension」がどのように機能するか、およびそれがどのように機能すると私が期待するかと矛盾していることです (他の人が自分の読みやすさに関する意見)。「変更されたクロージャへのアクセス」および「有害と見なされるループ変数のクローズ」に関するすべての質問を参照してください。これは、「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()));
}
FirstOrDefault()
で利用可能な拡張機能を使用できますIEnumerable<T>
。述語から戻ることによりfalse
、要素ごとに実行されますが、実際に一致が見つからなくてもかまいません。ToList()
これにより、オーバーヘッドが回避されます。
IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
LINQ で次のようなことをしたいのですが、方法がわかりません。
他の人が国内外で指摘しているIEnumerable
ように、LINQ とメソッドには副作用がないことが期待されています。
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))
可能性のリスト (完全に意図したものではありません) は延々と続きます。
Fredrik のメソッドを使用して、戻り値の型を変更しました。
このようにして、メソッドは他の LINQ メソッドと同様に遅延実行をサポートします。
編集:これが明確でない場合、このメソッドの使用は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 の列挙を強制するメソッドがないためです。
列挙ロールの役割を果たしたい場合は、各アイテムを譲渡する必要があります。
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;
}
}
}
Microsoft によるLINQ の Interactive Extensionsの実験的リリースがあります(これもNuGetにあります。その他のリンクについては、 RxTeams のプロファイルを参照してください)。チャンネル 9のビデオでよく説明されています。
そのドキュメントは XML 形式でのみ提供されます。より読みやすい形式にするために、このドキュメントを Sandcastle で実行しました。ドキュメント アーカイブを解凍し、 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 拡張機能を使用して反復のチェーンを設定し、それぞれのソースと条件によってコンテンツを定義する必要があります。式ツリーは強力で効率的ですが、その性質を理解することを学ぶ必要があります。また、遅延評価をオーバーライドして数文字を節約するためのプログラミングだけではありません。
多くの人がそれについて言及しましたが、私はそれを書き留めなければなりませんでした。これは最も明確で読みやすいものではありませんか?
IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();
短くてシンプル(st)。
多くの回答がすでに指摘しているように、そのような拡張メソッドを自分で簡単に追加できます。ただし、それをしたくない場合は、BCL でこのようなことを認識していませんが、Reactive Extensionへの参照が既にある場合 (およびSystem
参照していない場合) は、名前空間にまだオプションがあります。 、持っている必要があります):
using System.Reactive.Linq;
items.ToObservable().Subscribe(i => i.DoStuff());
メソッド名は少し異なりますが、最終結果はまさにあなたが探しているものです。
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
}
編集: Lazyバージョンは、このように 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;
}
}
Lazy バージョンは、たとえばToList () を具体化する必要があります。そうしないと、何も起こりません。以下の 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() の両方をライブラリに保持しています。
今、私たちには次の選択肢があります...
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
parallelOptions.MaxDegreeOfParallelism = 1;
#endif
Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));
もちろん、これは糸虫のまったく新しい缶を開きます.
ps (フォントについては申し訳ありません。システムが決定したものです)
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);
}
この「機能的アプローチ」の抽象化は、大きな時間をリークします。言語レベルでは、副作用を防ぐものは何もありません。コンテナ内のすべての要素に対してラムダ/デリゲートを呼び出すことができる限り、「ForEach」の動作が得られます。
たとえば、srcDictionary を destDictionary にマージする 1 つの方法 (キーが既に存在する場合 - 上書き)
これはハックであり、製品コードでは使用しないでください。
var b = srcDictionary.Select(
x=>
{
destDictionary[x.Key] = x.Value;
return true;
}
).Count();
私は、リンク拡張メソッドに副作用がないようにする必要があるという考えに敬意を表して同意しません (そうでないという理由だけでなく、どのデリゲートも副作用を実行できます)。
次の点を考慮してください。
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);
}
}
この例が示しているのは、アクションを定義する値をデコードして変換するための大きなスイッチ構成を記述する必要なく、要素のシーケンスに副作用を持つ多くの可能なアクションの 1 つを呼び出すことができる、一種の遅延バインディングです。それを対応するメソッドに入れます。
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;
}