Jest to znana pułapka dla osób, które moczyły stopy za pomocą LINQ:
public class Program
{
public static void Main()
{
IEnumerable<Record> originalCollection = GenerateRecords(new[] {"Jesse"});
var newCollection = new List<Record>(originalCollection);
Console.WriteLine(ContainTheSameSingleObject(originalCollection, newCollection));
}
private static IEnumerable<Record> GenerateRecords(string[] listOfNames)
{
return listOfNames.Select(x => new Record(Guid.NewGuid(), x));
}
private static bool ContainTheSameSingleObject(IEnumerable<Record>
originalCollection, List<Record> newCollection)
{
return originalCollection.Count() == 1 && newCollection.Count() == 1 &&
originalCollection.Single().Id == newCollection.Single().Id;
}
private class Record
{
public Guid Id { get; }
public string SomeValue { get; }
public Record(Guid id, string someValue)
{
Id = id;
SomeValue = someValue;
}
}
}
Spowoduje to wydrukowanie „Fałsz”, ponieważ dla każdej nazwy podanej w celu utworzenia oryginalnej kolekcji funkcja select jest ciągle ponownie oceniana, a wynikowy Record
obiekt jest tworzony od nowa. Aby to naprawić, ToList
na końcu można dodać proste wezwanie do GenerateRecords
.
Jaką korzyść Microsoft miał nadzieję uzyskać, wdrażając go w ten sposób?
Dlaczego implementacja po prostu nie buforuje wyników w wewnętrznej tablicy? Jedną konkretną częścią tego, co się dzieje, może być odroczenie wykonania, ale można to nadal wdrożyć bez tego zachowania.
Po dokonaniu oceny danego członka kolekcji zwróconej przez LINQ, jaką korzyść zapewnia nie zachowanie wewnętrznego odwołania / kopii, ale ponowne obliczenie tego samego wyniku, jako zachowanie domyślne?
W sytuacjach, w których istnieje szczególna potrzeba logiki dla tego samego elementu kolekcji, który jest wielokrotnie przeliczany, wydaje się, że można to określić za pomocą opcjonalnego parametru, a domyślne zachowanie może zrobić inaczej. Ponadto przewaga prędkości uzyskana dzięki odroczonemu wykonaniu jest ostatecznie zmniejszana o czas potrzebny do ciągłego przeliczania tych samych wyników. Wreszcie jest to mylący blok dla tych, którzy są nowi w LINQ, i może prowadzić do subtelnych błędów w ostatecznie czyimś programie.
Jaka jest z tego korzyść i dlaczego Microsoft podjął tę pozornie bardzo świadomą decyzję?
return listOfNames.Select(x => new Record(Guid.NewGuid(), x)).ToList();
To daje „kopię w pamięci podręcznej”. Problem rozwiązany.