Twoje pytanie, jak rozumiem, wydaje się opierać na niewłaściwym założeniu. Zobaczmy, czy mogę zrekonstruować rozumowanie:
- Artykuł z linkami opisuje, w jaki sposób automatycznie generowane sekwencje wykazują zachowanie „leniwe” i pokazuje, jak może to prowadzić do sprzecznych z intuicją wyników.
- Dlatego mogę wykryć, czy dana instancja IEnumerable będzie wykazywać to leniwe zachowanie, sprawdzając, czy jest ona generowana automatycznie.
- Jak mogę to zrobić?
Problem polega na tym, że druga przesłanka jest fałszywa. Nawet gdybyś mógł wykryć, czy dany IEnumerable był wynikiem transformacji bloku iteratora (i tak, istnieją sposoby, aby to zrobić), nie pomogłoby to, ponieważ założenie jest błędne. Zilustrujmy dlaczego.
class M { public int P { get; set; } }
class C
{
public static IEnumerable<M> S1()
{
for (int i = 0; i < 3; ++i)
yield return new M { P = i };
}
private static M[] ems = new M[]
{ new M { P = 0 }, new M { P = 1 }, new M { P = 2 } };
public static IEnumerable<M> S2()
{
for (int i = 0; i < 3; ++i)
yield return ems[i];
}
public static IEnumerable<M> S3()
{
return new M[]
{ new M { P = 0 }, new M { P = 1 }, new M { P = 2 } };
}
private class X : IEnumerable<M>
{
public IEnumerator<X> GetEnumerator()
{
return new XEnum();
}
// Omitted: non generic version
private class XEnum : IEnumerator<X>
{
int i = 0;
M current;
public bool MoveNext()
{
current = new M() { P = i; }
i += 1;
return true;
}
public M Current { get { return current; } }
// Omitted: other stuff.
}
}
public static IEnumerable<M> S4()
{
return new X();
}
public static void Add100(IEnumerable<M> items)
{
foreach(M item in items) item.P += 100;
}
}
W porządku, mamy cztery metody. S1 i S2 są automatycznie generowanymi sekwencjami; S3 i S4 są sekwencjami generowanymi ręcznie. Załóżmy teraz, że mamy:
var items = C.Sn(); // S1, S2, S3, S4
S.Add100(items);
Console.WriteLine(items.First().P);
Wynik dla S1 i S4 wyniesie 0; za każdym razem, gdy wyliczasz sekwencję, dostajesz nowe odniesienie do utworzonego M. Wynik dla S2 i S3 wyniesie 100; za każdym razem, gdy wyliczasz sekwencję, dostajesz to samo odniesienie do M, które dostałeś ostatni raz. To, czy kod sekwencji jest generowany automatycznie, czy nie, jest prostopadłe do pytania, czy wyliczone obiekty mają tożsamość referencyjną, czy nie. Te dwie właściwości - automatyczne generowanie i tożsamość referencyjna - w rzeczywistości nie mają ze sobą nic wspólnego. Artykuł, który podlinkowałeś, nieco je łączy.
O ile dostawca sekwencji nie jest udokumentowany jako zawsze oferujący obiekty, które mają tożsamość referencyjną , nie należy zakładać, że tak jest.
ICollection<T>
byłby lepszym wyborem, ponieważ nie wszystkie kolekcje sąList<T>
. Na przykład tablicePoint[]
implementują,IList<T>
ale nie sąList<T>
.