Вместо того, чтобы думать об этом как о специальном классе, реализующем IDisposable
, подумайте о том, на что это было бы похоже с точки зрения нормального выполнения программы:
Directory dir = Directory.CreateDirectory(path);
try
{
string fileName = Path.Combine(path, "data.txt");
File.WriteAllText(fileName, myData);
UploadFile(fileName);
File.Delete(fileName);
}
finally
{
Directory.Delete(dir);
}
Как это должно себя вести? Это точно такой же вопрос. Вы оставляете содержимое блока finally
как есть, тем самым потенциально маскируя исключение, которое происходит в блоке try
, или вы заключаете Directory.Delete
в свой собственный блок try-catch
, поглощая любое исключение по порядку предотвратить маскировку оригинала?
Я не думаю, что есть правильный ответ - факт в том, что вы можете иметь только одно окружающее исключение, поэтому вы должны выбрать одно. Однако .NET Framework устанавливает некоторые прецеденты; один пример - сервисные прокси WCF (ICommunicationObject
). Если вы попытаетесь Dispose
неисправный канал, он выдаст исключение, и будет замаскировать любое исключение, которое уже находится в стеке. Если я не ошибаюсь, TransactionScope
тоже может это сделать.
Конечно, само это поведение в WCF было бесконечным источником путаницы; большинство людей на самом деле считают это очень раздражающим, если не сломленным. Google "WCF dispose mask", и вы поймете, что я имею в виду. Поэтому, возможно, мы не всегда должны пытаться делать то же самое, что делает Microsoft.
Лично я считаю, что Dispose
никогда не должен маскировать исключение, уже находящееся в стеке. Оператор using
фактически является блоком finally
и большую часть времени (всегда есть крайние случаи), вы не хотели бы генерировать (и не перехватывать) исключения в блоке finally
, или. Причина просто отладка; может быть крайне трудно понять суть проблемы - особенно проблемы в производстве, когда вы не можете пройтись по источнику - когда у вас даже нет возможности выяснить, где именно приложение не работает. Я был в этом положении и могу с уверенностью сказать, что это сведет вас с ума полностью и безумно.
Я бы порекомендовал либо съесть исключение в Dispose
(зарегистрировать его, конечно), либо проверить, действительно ли вы уже находитесь в сценарии раскрутки стека из-за исключения и есть только последующие исключения, если вы знаете, что будете их маскировать. Преимущество последнего состоит в том, что вы не едите исключения, если вам действительно не нужно; недостаток в том, что вы ввели в свою программу недетерминированное поведение. Еще один компромисс.
Большинство людей, вероятно, просто пойдут с первым вариантом и просто скроют все исключения, возникающие в finally
(или using
).