לדלג לתוכן

פתרון כשל באמצע רכישה

הרשימה המקומית שומרת את המשאבים שהרכישה שלהם הסתיימה. פעולת הניקוי מסירה אותם מן הרשימה גם אם Dispose נכשל, כך שלא נטען בהמשך שהמשאב עדיין בבעלות המעטפת. כשל שחרור אינו מוכיח שהמשאב נשאר פתוח או נסגר; המדיניות כאן היא ניסיון אחד ודיווח מפורש.

using var owner = Bundle.Create(i => new FakeResource(i), failBefore: 0);

sealed class Bundle : IDisposable
{
    private readonly List<IDisposable> items;
    private Bundle(List<IDisposable> items) => this.items = items;
    public static Bundle Create(Func<int, IDisposable> acquire, int failBefore)
    {
        var items = new List<IDisposable>(3);
        try
        {
            for (int i = 1; i <= 3; i++)
            {
                if (i == failBefore) throw new IOException($"acquire {i}");
                items.Add(acquire(i));
            }
            return new Bundle(items);
        }
        catch (Exception error)
        {
            var errors = new List<Exception> { error };
            Drain(items, errors);
            throw new AggregateException(errors);
        }
    }
    public void Dispose()
    {
        var errors = new List<Exception>();
        Drain(items, errors);
        if (errors.Count != 0) throw new AggregateException(errors);
    }
    private static void Drain(List<IDisposable> items, List<Exception> errors)
    {
        while (items.Count != 0)
        {
            int last = items.Count - 1;
            IDisposable item = items[last];
            items.RemoveAt(last);
            try { item.Dispose(); }
            catch (Exception error) { errors.Add(error); }
        }
    }
}
sealed class FakeResource(int id) : IDisposable
{
    public void Dispose() => Console.WriteLine($"dispose {id}");
}

הכשל לפני רכישה ראשונה משחרר אפס משאבים, לפני השנייה משחרר את הראשון, ולפני השלישית משחרר שניים בסדר 2 ואז 1. במקרה הצלחה השחרור הוא 3, 2, 1. אפשר להוסיף בדיקה של כשל בתוך acquire עצמו; אם הוא יצר משאב חלקי לפני שזרק, האחריות לניקוי הפנימי שלו נשארת שלו. הפתרון אינו מתיימר להתאושש מ־OutOfMemoryException בכל פעולה של בניית רשימת חריגות, ואינו חוזה לעבודה מקבילית.