LoginSignup
4
2

More than 3 years have passed since last update.

【C#】IEnumerableの遅延評価でプロパティが書き換わらない

Last updated at Posted at 2020-07-12

プロパティの値を変更したけど変わらない・・・

職場においてC#を利用して自社サービスを実装しているのですが、
IEnumerableではまったことをちょっとまとめておきたいと思います。

下記は起こった事象を簡単にしたものとなります。下記の例は、IEnumerableに入ったユーザ情報の名前が大文字になることを期待したものとなります。


// このようなクラスを読み込んでいます
//class User {
//    public string Name { get; set; }
//}

IEnumerable<User> users = new[] { "kato", "saito", "kondo" }
    .Select(n => {
        return new User { Name = n };
    });

foreach (User user in users) {
    // 大文字に変換したはずなのに・・・
    user.Name = user.Name.ToUpper();
}

foreach (User user in users) {
    Console.WriteLine(user.Name);
}

出力結果

kato
saito
kondo

なぜか最初の値のままでした・・・

そうなんです。参照が違っておりました。
Enumerable.RepeatとかEnumerable.Selectとかが遅延評価のIEnumerableを返すので、foreachToListで評価された際に初期化されてしまいます。

修正パターン1


IEnumerable<User> users = new[] { "kato", "saito", "kondo" }
    .Select(n => {
        return new User { Name = n };
    }).ToList();

foreach (User user in users) {
    // 大文字に変換
    user.Name = user.Name.ToUpper();
}

foreach (User user in users) {
    Console.WriteLine(user.Name);
}

ToList()することで再度評価されても同じ参照が返るので無事、大文字にできました。

KATO
SAITO
KONDO

修正パターン2

修正パターン1だと、2ループしてしまうのは性能上問題があると思いますので、下記の方法を考えました。


IEnumerable<User> users = new[] { "kato", "saito", "kondo" }
    .Select(n => {
        return new User { Name = n };
    });

users = users.Select(u => {
    u.Name = u.Name.ToUpper();
    return u;
});

foreach (User user in users) {
    Console.WriteLine(user.Name);
}

初期化の後にSelect()に修正して、無事、

KATO
SAITO
KONDO

大文字に置換することができました!

実行環境

  • C# 7.3
  • Windows 10
4
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2