פתרון חיי סגירות וסדרות¶
ה־finally שייך לחיי ה־enumerator, וה־using החיצוני שייך לחיי ה־reader. ההפרדה מאפשרת לצרכן להמשיך לקרוא אחרי שסיים מעבר חלקי.
using var reader = new StringReader("first\nsecond\n");
foreach (string line in Lines(reader))
{
Console.WriteLine(line);
break;
}
Console.WriteLine(reader.ReadLine());
static IEnumerable<string> Lines(TextReader reader)
{
try
{
string? line;
while ((line = reader.ReadLine()) is not null) yield return line;
}
finally { Console.WriteLine("enumerator disposed"); }
}
המינוי הבא שומר את זהות ה־handler ומנתק אותה פעם אחת. הקוד הוא הגדרת טיפוסים לצירוף לאותה תוכנית, והבדיקה תיצור Publisher ותפעיל Raise לפני Dispose ואחריו.
sealed class Publisher
{
public event Action? Tick;
public void Raise() => Tick?.Invoke();
}
sealed class Subscriber : IDisposable
{
private Publisher? publisher;
private readonly Action handler;
private readonly byte[] buffer = new byte[1_000_000];
public int Calls { get; private set; }
public Subscriber(Publisher publisher)
{
this.publisher = publisher;
handler = OnTick;
publisher.Tick += handler;
}
private void OnTick() { Calls++; buffer[0]++; }
public void Dispose()
{
if (publisher is null) return;
publisher.Tick -= handler;
publisher = null;
}
}
הגרף שלפני הניתוק עובר מן המפרסם אל delegate שה־Target שלו הוא Subscriber. אחרי הניתוק ה־publisher אינו מחזיק אותו, אם אין references אחרים. בתיקון הלולאה נוסיף int captured = i בתוך גוף הלולאה וניצור () => captured. ההעתקה של רשימת delegates אינה משכפלת את תאי הסגירה שלהם, ולכן היא אינה משנה את התוצאה בעצמה.