閉まっている。この質問はさらに焦点を絞る必要があります。現在回答を受け付けておりません。
この質問を改善したいですか?この投稿を編集して、1 つの問題のみに焦点を当てた質問を更新してください。
3 年前
に閉鎖されました。この質問を改善してください
ループから Linq ステートメントに短縮するコードがいくつかありますが、そうするのは危険だと感じます。
同じステートメント内で、前の .Where 句 (TryParse を使用) で設定された out パラメータ値を .Select しても安全ですか?私が尋ねる主な理由は、Where 句で保証されているはずなのに、Select ステートメントで「ローカル変数 val はアクセス前に初期化できない可能性がある」というコンパイル エラーが発生したため、val に「」を代入しなければならなかったということです。ダミー」これをコンパイルするために値を取得します。
var items = new List<string>();
// out parameters don't have to be assigned first, but the compiler forced me to.
int val = 0;
var result = items
.Where(item => int.TryParse(item, out val))
.Select(i => val)
.ToList();
私の限られたテストでは、.ToList を削除して値を変更した場合でも、正しい結果が得られるようです。f 値
何か「落とし穴」はありますか?ここで?
3
それは悪い考えのようです(tm)。むしろ items.Select(i => new { gotIt: dict.TryGetValue(i, out string val), gotWhat: val }).Where(i => i.gotIt).Select(i = > i.gotWhat).ToList();念のため
– 異端の猿
2020 年 9 月 3 日 21:43
1
Where 句で保証されている必要があるにもかかわらず、Where のコードが確実に発生することを認識できるほどコンパイラは賢明ではないため、警告が表示されます。
– mjwills
2020 年 9 月 3 日 21:45
3
この例は実際にはここの別の質問からのものですが、主に Where で out パラメータを渡し、それを Select で使用するパターンについて質問しています。うまくいきますか?通常、はい(すべて LINQ to Objects であり、ソート直後にそれを読み取っている限り)などは混乱することはありません - たとえば、EF では明らかに機能しません)。それは良いアイデアですか?代替手段が利用できる場合は、それを避けたいと思います。
– mjwills
2020 年 9 月 3 日 21:50
3
AsParallel は、@RufusL の現在のアプローチで大混乱を引き起こす可能性があるものの例です。
– mjwills
2020 年 9 月 3 日 21:58
3
私ならそんなことはしません。ここで質問しなければならないという事実は、それがどのように動作するかがすぐには明らかではないことを示しています。正直に言うと、代わりに foreach を使用することに何の問題もありません。 LINQ が必ずしも優れているわけではありません。
– ジョン H
2020 年 9 月 3 日 21:59
------------------------
安全ではないと感じますが、失敗する状況を作るのに苦労しました単一のスレッドではありません。
マルチスレッドにすると、いずれにしてもすべての賭けは失敗します。
Select が Where の直後に続き、val 変数の中間操作がないため、この特定の例では安全です。
ただし、この種のことは簡単に失敗する可能性があるため、優れたプログラマーとして、LINQ 式で外部状態を回避する必要があることを私たちは知っています。
幸いなことに、これを回避する方法はいくつかあります。私のお気に入りはこれです:
var result =
items
.SelectMany(item =>
int.TryParse(item, out int val)
? new[] { val }
: Enumerable.Empty<int>())
.ToList();
または:
var result =
items
.Select(item =>
int.TryParse(item, out int val)
? (int?)val
: (int?)null)
.Where(val => val.HasValue)
.Select(val => val.Value)
.ToList();
元のコードを使用したいが、安全であることを確認したい場合は、次の簡単な拡張メソッドを使用してください。
public static IEnumerable<R> Capture<T, R>(
this IEnumerable<T> source,
Func<IEnumerable<T>, IEnumerable<R>> transform)
{
foreach (var r in transform(source))
{
yield return r;
}
}
これは次のように使用できます:
var result =
items
.Capture(xs =>
{
int val = 0;
return
xs
.Where(item => int.TryParse(item, out val))
.Select(i => val);
});
2
マルチスレッドにすると、とにかくすべてが無駄になります。公平を期すために言うと、これは共有状態を使用しているためだけです (つまり、共有状態を使用しないことの十分な根拠です)。 LINQ ステートメントの外側に変数がなければ、並列処理は通常非常に安全です...
– mjwills
2020 年 9 月 3 日 23:03
1
@mjwills - はい、そうですね。それは本当です。私はマルチスレッドのケースを検討したことを言いたかっただけですが、それ以上立ち入るつもりはありませんでした。
– 謎めいたもの
2020 年 9 月 3 日 23:29
------------------------
これはおそらく機能しますが、安全ではありません。これはより安全な代替手段です。
/// <summary>A generic delegate for <see cref="selectWhere{TSource, TResult}(IEnumerable{TSource}, pfnSelectWhere{TSource, TResult})" /></summary>
public delegate bool pfnSelectWhere<TSource, TResult>( TSource source, out TResult result );
/// <summary>Combined Where + Select extension method for lazily-evaluated sequences</summary>
public static IEnumerable<TResult> selectWhere<TSource, TResult>( this IEnumerable<TSource> source,
pfnSelectWhere<TSource, TResult> selector )
{
foreach( var i in source )
if( selector( i, out var r ) )
yield return r;
}
// Usage example
static void Main( string[] args )
{
var items = new List<string>();
List<int> result = items
.selectWhere( ( string s, out int v ) => int.TryParse( s, out v ) )
.ToList();
}