LINQで以下と同等のことを実行したいのですが、方法がわかりません。
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
実際の構文は何ですか?
foreach (var i in items) i.Dostuff();
LINQで以下と同等のことを実行したいのですが、方法がわかりません。
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
実際の構文は何ですか?
foreach (var i in items) 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);
}
}
IENumerable<T>
拡張メソッドでを返す必要があります。例:public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Fredrikが修正を提供しましたが、これが最初からフレームワークにない理由を検討する価値があるかもしれません。この考えは、LINQクエリ演算子は副作用がなく、世界を見るのに適度に機能する方法に適合しているべきだと考えています。明らかにForEachは正反対であり、純粋に副作用ベースの構造です。
これが悪いことだと言っているのではなく、決定の背後にある哲学的理由について考えているだけです。
Seq.iter
何も返されません。副作用の要素を持つ新しいシーケンスは言うまでもありません。それは本当に副作用についてです。は、の拡張メソッドとまったく同じSeq.map
であるため、考えているかもしれません。Seq.iter
ForEach
IEnumerabe<_>
アップデート 7/17/2012:どうやら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のみを使用しています。これは、このようなことをするために完全にフェッチされているわけではないためです。明らかに、このオブザーバブルを作成するためのより良い方法があり、私はポイントを示すことだけを試みています。通常、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、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; });
foreach(Item i in GetItems()) { i.DoStuff();}
あなたはより多くの文字を取って、それは非常に混乱製
items.All(i => { i.DoStuff(); return true; }
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に列挙を強制するメソッドがないためです。
ForEach
は興味深いですが、の動作と一致しません。List.ForEach
その署名はですpublic void ForEach(Action<T> action)
。また、署名がであるObservable.ForEach
への拡張機能の動作と一致しません。Scalaコレクションの同等のものであるFWIWは、遅延のあるものであっても、C#のvoidと同等の戻り型を持っています。IObservable<T>
public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext)
ForEach
Select
とまったく同じように機能しToList
ます。の目的はForEach
呼び出す必要がないことToList
です。すぐに実行されます。
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;
}
}
}
MicrosoftによるLINQへのInteractive Extensionsの試験的なリリースがあります(NuGetでも、リンクについてはRxTeamsのプロファイルを参照してください)。チャンネル9のビデオは、それをよく説明しています。
そのドキュメントはXML形式でのみ提供されます。このドキュメントをサンドキャッスルで実行して、より読みやすい形式にすることができます。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拡張機能を使用する必要があります。式ツリーは強力で効率的ですが、その性質を理解することを学ぶ必要があります。そして、遅延評価を上書きするいくつかの文字を保存するために、それらの周りのプログラミングだけではありません。
多くの人がそれについて言及しましたが、私はそれを書き留めなければなりませんでした。これは最も明確/最も読みやすいですか?
IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();
短くてシンプル(st)。
GetItems()
メソッドが返されるかが明確になります。
foreach (var item in GetItems()) item.DoStuff();
は単なる美しさです。
今、私たちはオプションがあります...
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
parallelOptions.MaxDegreeOfParallelism = 1;
#endif
Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));
もちろん、これはまったく新しいスレッドワームの缶を開きます。
ps(フォントについて申し訳ありません。システムが決定したものです)
多くの回答がすでに指摘しているように、そのような拡張メソッドを自分で簡単に追加できます。ただし、これを実行したくない場合は、BCLでこのようなことは何も認識していませんSystem
が、既にReactive Extensionへの参照がある場合(および、あなたが持っているべきです):
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();});
実装の熱心なバージョン。
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
}
編集:レイジーバージョンは次のように、歩留まりのリターンを使用して、この。
public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
foreach (var item in enu)
{
action(item);
yield return item;
}
}
レイジーバージョンは具体化する必要があります(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()の両方をライブラリに保持しています。
foreach(T item in enu) {action(item); yield return item;}
foreach (T item in enu) { action1(item); action2(item); action3(item); }
?単一の明示的なループ。action2を開始する前に、すべてのaction1 を実行することが重要だったと思います。
ProcessShipping()
か?あなたは買い手にメールを送り、彼のクレジットカードに請求しますが、彼のものを決して送らないのですか?確かに問題を求めています。
この「機能的アプローチ」の抽象化は、大きな時間を浪費します。言語レベルでは何も副作用を防ぎません。コンテナ内のすべての要素に対してラムダ/デリゲートを呼び出すことができる限り、「ForEach」動作が得られます。
たとえば、srcDictionaryをdestDictionaryにマージする1つの方法(キーがすでに存在する場合-上書き)
これはハックであり、実稼働コードでは使用しないでください。
var b = srcDictionary.Select(
x=>
{
destDictionary[x.Key] = x.Value;
return true;
}
).Count();
foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }
ますか?量産コードでない場合、どこで、なぜこれを使用する必要がありますか?
ジョン・スキートに触発されて、私は彼の解決策を次のように拡張しました:
拡張方法:
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);
}
MoreLinqにはIEnumerable<T>.ForEach
他にも便利な拡張機能がたくさんあります。おそらく、だけで依存関係をとる価値はありませんForEach
が、そこには多くの便利な機能があります。
リンク拡張メソッドには副作用がないという考えには敬意を払いません(そうでない場合だけでなく、どのデリゲートも副作用を実行できます)。
以下を検討してください。
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つを呼び出すことができる一種の遅延バインディングです。対応するメソッドに入れます。
さらに別の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()
。