🚀
LINQ の遅延評価わかってなかった
ちょっと複雑なオブジェクトを弄っているときに、え?なんでこれ値が入らないんだ?
ってなったので、調査したら、全然自分の認識と違ったのでメモです。
検証で使った環境
- LinqPad7
- .NET : 3.1~7.0
TL;DR
LINQ、必要な場合はToArray()とかして、よきタイミングで明示的に評価しろ。
Selectの途中で、値吸い出そうとしたらできなかった
「なんでそんなことした??」ってのは置いておいて(やらないと大変な場合があったんです)、
以下のコードをご覧ください。
実際に悩んでたコードと似たことをするコードです。
コロン区切りの文字列配列を突っ込んだら、Key==Cの値と、KeyとValueのペアを得るコードです。
object SplitColonKV(string [] dataStrings)
{
string cText = "";
var keyValues = dataStrings
.Select(x => {
var keyValue = x.Split(':');
var key = keyValue.FirstOrDefault();
var value = keyValue.ElementAtOrDefault(1);
if(key == "c")
{
cText = value;
}
return new
{
Key = key,
Value = value
};
});
return new
{
cText,
keyValues,
};
}
void Main()
{
var dataStrings = new string[]
{
"a:aaaaaa",
"b:bbbbbb",
"c:cccccc",
"d:dddddd",
"e:eeeeee",
};
SplitColonKV(dataStrings)
.Dump();
}
では、これを実行してみましょう。
理想というか、想像はこうなりますね。
しかしどうでしょう、実際の実行結果はこうでした。
は??
cText おまえなんで値入ってないん???
遅延評価やで
なんかいろいろ調べて、冷静になったら、そらそうやろってなりました。
keyValues の値は return new するときまで評価されません。
ということは、return new するまで cText には何も代入されないので、cText は宣言されたときの値のままです。
なので、return new する前にToArrayとかして明示的に評価してあげます。
object SplitColonKV(string [] dataStrings)
{
string cText = "";
var keyValues = dataStrings
.Select(x => {
var keyValue = x.Split(':');
var key = keyValue.FirstOrDefault();
var value = keyValue.ElementAtOrDefault(1);
if(key == "c")
{
cText = value;
}
return new
{
Key = key,
Value = value
};
})
.ToArray();
return new
{
cText,
keyValues,
};
}
void Main()
{
var dataStrings = new string[]
{
"a:aaaaaa",
"b:bbbbbb",
"c:cccccc",
"d:dddddd",
"e:eeeeee",
};
SplitColonKV(dataStrings)
.Dump();
}
結果↓
まとめ
雰囲気でC#やってることがバレました。精進します。
Discussion