Andrey on .NET | IDisposable.Dispose() – определяем исключение в using

IDisposable.Dispose() – определяем исключение в using

Вчера в комментариях заметки "Отложенные действия в C#" был задан вопрос про исключения. В нем подразумевалась необходимость определять в методе Dispose() нормально ли завершился код блока using. При этом сам метод вызывается до передачи управления блоку catch. Так есть ли решение?

Логично предположить, что раз эта заметка опубликована, то и решение есть. Поэтому сразу посмотрим его на примере простой реализации IDisposable.

using System;
using System.Runtime.InteropServices;

/// <summary>Test class.</summary>
public class DisposableObject : IDisposable
{
    /// <summary>IDisposable implementation that checks for an exception.</summary>
    void IDisposable.Dispose()
    {
        // Check for an exception
        if (Marshal.GetExceptionCode() != 0) {
            Console.WriteLine("Dispose() > Exception detected!");
            return;
        }

        Console.WriteLine("Dispose() > Completed successfully!");
    }

    /// <summary>Throwing demo exception.</summary>
    public void ExceptionTest()
    {
        Console.WriteLine("IDisposableObject.ExceptionTest() > throwing exception ...");
        throw new NotImplementedException();
    }
}

Для определения исключения используется метод класса Marshal

public static int GetExceptionCode();

Он возвращает код типа исключения, который при нормальном выполнении программы будет равен нулю. Остальные значения будем считать просто признаком наличия исключения.

Для проверки создадим простое консольное приложение:

using System;

class Program
{
    static void Main(string[] args)
    {
        try {
            using (DisposableObject obj = new DisposableObject()) {
                throw new InvalidCastException();
                // obj.ExceptionTest();
            }
        }
        catch (Exception ex) {
            Console.WriteLine("try-catch > Exception caught!\n\n{0}", ex.ToString());
        }

        Console.ReadKey(true);
    }
}

Комментарии тут не требуются.

Исходный код (C#, Visual Studio 2010):
IDisposableExceptionDemo.zip (8.74 kb)

Комментарии (2) -

Интересно, не знал о такой возможности. Только вот вопрос – где это необходимо применять? Ведь обычно класс который создавался в <code>using</code>-е не используется дальше него, и если в нем произошла ошибка, то нам не должно быть жалко его выбрасывать. А если жалко – нужно ловить исключение на уровне вызова.

Думаю что формулировать так вопрос "где это необходимо" не совсем корректно. Точнее будет спросить – где это применимо?

Сама возможность, как я написал выше, мне стала интересна после вопроса в заметке про отложенные действия. Дальнейшее относится к отложенным действиям, равно как и к другим классам, использующим using для создания более читабельного кода. В них возможна ситуация, когда действия в Dispose() зависят от успешно ли были завершены все действия в блоке using. В том же комментарии пример с сортировкой. При этом сам объект создается до блок и остается жить после.

Добавить комментарий