2015-11-20 14 views
1

Представьте, что на C# (в .NET 4.5.x) у вас есть блок using вокруг объекта, который реализует IDisposible. Представьте себе, чтоЛовля исключений в Dispose во время исключения

  1. исключение выбрасывается в использовании блока
  2. когда метод Dispose называется, дополнительное исключение

Каков наилучший способ передать два исключения глобальной обработчик ошибок?

Пример кода:

class SampleClass : IDisposable 
{ 
    void doSomething() 
    { 
     // Imagine that this code is actually doing something 
     // Except that it unexpectedly hits an exception 
     throw new Exception("Exception A"); 
    } 

    void Dispose() 
    { 
     // Imagine that this code is doing some cleanup 
     // Except that it is buggy 
     // And throws more exceptions 
     throw new Exception("Exception B"); 
    } 
} 

static void Main() 
{ 
    try 
    { 
     using(var c = new SampleClass()) 
     { 
      c.doSomething(); 
     } 
    } 
    catch (Exception e) 
    { 
     // Code that records and reports exceptions 
     RecordException(e); 
    } 
} 

В приведенном выше примере, я хочу, чтобы все детали как Exception A и Exception B записаны. Каков наилучший способ передать два исключения в RecordException?

This article предлагает расширение SafeUsingBlock. Это установленная передовая практика?

Edit: В этом случае я контролировать и может повторно написать SampleClass и SampleClass.Dispose()

+0

Вы можете создать свой собственный обработчик глобального исключения и/или чешский из Log4Net, довольно простой способ войти вещи в содержание вашего сердца. –

+1

Dispose() никогда не должен генерировать исключение. Попытка рассуждать «что, если это так или иначе» довольно бесполезно и никогда не заставит вас прийти к правильному решению. Вам придется отложить с помощью критического финализатора, который вряд ли удастся выработать, или вытащить пробку с помощью Environment.FailFast(). Выньте вилку. –

+0

@ HansPassant - см. Мою заметку ниже о '' Task.Dispose() '' –

ответ

1

Если у вас есть контроль метода Dispose(), я бы предложил сделать это ошибка-доказательство любой ценой. В общем, вы должны надежно полагаться на IDisposable s на Dispose() безопасно.

Если это класс, который вы не имеете никакого контроля над, ваше предложение о try оберточной using будет работать, хотя, чтобы сделать его более изящным, я бы, вероятно, сделать что-то ближе к:

try 
{ 
    var c = new SampleClass(); 
    c.doSomething(); 
    c.Dispose(); 
} 
catch(Exception e) 
{ 
    RecordException(e); 
} 

SafeUsingBlock() выглядит например, он выполняет свою работу, но я бы не счел, что это была лучшая практика: это обходной путь. Установленная наилучшая практика заключается в том, чтобы методы Dispose() были безопасными.

Кроме того, если SampleClass это один вы не имеете никакого контроля над, рассмотрим окружив его фасада, который также реализует IDisposable и обрабатывает ошибки в методе Dispose(), например,

public class SampleClassWrapper : IDisposable 
{ 
    private readonly SampleClass _target; 
    public SampleClassWrapper(SampleClass target) 
    { 
     _target = target; 
    } 

    public void doSomething() 
    { 
     _target.doSomething(); 
    } 

    public void Dispose() 
    { 
     try 
     { 
      _target.Dispose(); 
     } 
     catch(Exception e) 
     { 
      RecordException(e); 
     } 
    } 
} 

Тогда вы можете смело использовать свой SampleClassWrapper в using блоке, не заботясь о сбоях в Dispose()

+0

В целом, я считаю, что следует надежно полагаться на '' Dispose() '' безопасно. Однако [Task.Dispose()] (https://msdn.microsoft.com/en-us/library/dd270681 (v = vs.110) .aspx) выдаст исключение, если задача не будет завершена. Таким образом, если '' SampleClass'' принадлежит объект '' Task'' (т.'' private Task _task; '', '' SampleClass'' отвечает за его удаление. Таким образом, '' SampleClass.Dispose() '' будет иметь что-то вроде '' if (_task! = Null) {_task.Dispose()} '', что вызовет исключение, если '' SampleClass'' имеет ошибки в отмене логики '' _task'' в случае сбоя в '' doSomething'' –

+0

Ах, это имеет смысл - но в этой ситуации вы выходите за пределы использования пути, предназначенного для использования предоставленного 'Dispose()' поведения , поэтому не может быть и речи о какой-то тупой структуре управления. Кроме того, есть ли еще один путь, который вы можете преследовать, чтобы опереться на библиотеку 'Task'? Например, если ваше приложение завершает работу и вы очищаете 'Task', подключите некоторые [' CancellationToken'] (https://msdn.microsoft.com/en-us/library/dd997396 (v = vs. 110) .aspx) s и отменить их при поддержке каркаса? – eouw0o83hf