Во-первых, исключения никогда не должны использоваться в качестве механизма управления потоком. Исключения составляют механизм распространения и обработки ошибок, но их никогда нельзя использовать для управления потоком программ. Поток управления - это область условных операторов и циклов. Это часто является критическим заблуждением, которое делают многие программисты, и, как правило, приводит к таким кошмарам, когда они пытаются справиться с исключениями.
В языке, подобном C #, который предлагает структурированную обработку исключений, идея состоит в том, чтобы позволить «исключительным» случаям в вашем коде идентифицироваться, распространяться и со временем обрабатываться. Обработка, как правило, остается на высшем уровне вашего приложения (то есть клиент Windows с пользовательским интерфейсом и диалогами ошибок, веб-сайт со страницами ошибок, средство ведения журнала в цикле сообщений фоновой службы и т. Д.) В отличие от Java, который использует проверенная обработка исключений, C # не требует от вас специально обрабатывать каждое исключение, которое может пройти через ваши методы. Напротив, попытка сделать это, несомненно, приведет к серьезным узким местам в производительности, поскольку отлов, обработка и, возможно, повторное создание исключений - дорогостоящее дело.
Общая идея с исключениями в C # заключается в том, что если они случаются ... и я подчеркиваю , если , потому что они называются исключениями из-за того факта, что во время нормальной работы вы не должно возникать никаких исключительных условий , ... если они случаются, у вас есть инструменты для безопасного и аккуратного восстановления и представления пользователя (если он есть) с уведомлением о сбое приложений и возможных вариантах разрешения.
В большинстве случаев хорошо написанное приложение на C # не будет иметь столько блоков try / catch в основной бизнес-логике, и будет иметь гораздо больше вариантов try / finally или, что еще лучше, с использованием блоков. Для большинства кода проблема в ответе на исключение состоит в том, чтобы нормально восстановиться путем освобождения ресурсов, блокировок и т. Д. И разрешения продолжения исключения. В вашем коде более высокого уровня, обычно во внешнем цикле обработки сообщений приложения или в стандартном обработчике событий для систем, таких как ASP.NET, вы в конечном итоге выполните структурированную обработку с помощью try / catch, возможно, с несколькими предложениями catch для обработки конкретных ошибок, которые требуют уникальной обработки.
Если вы правильно обрабатываете исключения и строите код, который использует исключения соответствующим образом, вам не нужно беспокоиться о большом количестве блоков try / catch / finally, кодов возврата или сложных извлечений методов с большим количеством ref и out параметры. Вы должны увидеть код, похожий на этот:
public void ClientAppMessageLoop()
{
bool running = true;
while (running)
{
object inputData = GetInputFromUser();
try
{
ServiceLevelMethod(inputData);
}
catch (Exception ex)
{
// Error occurred, notify user and let them recover
}
}
}
// ...
public void ServiceLevelMethod(object someinput)
{
using (SomeComponentThatsDisposable blah = new SomeComponentThatsDisposable())
{
blah.PerformSomeActionThatMayFail(someinput);
} // Dispose() method on SomeComponentThatsDisposable is called here, critical resource freed regardless of exception
}
// ...
public class SomeComponentThatsDisposable: IDosposable
{
public void PErformSomeActionThatMayFail(object someinput)
{
// Get some critical resource here...
// OOPS: We forgot to check if someinput is null below, NullReferenceException!
int hash = someinput.GetHashCode();
Debug.WriteLine(hash);
}
public void Dispose()
{
GC.SuppressFinalize(this);
// Clean up critical resource if its not null here!
}
}
Следуя вышеприведенной парадигме, вы не имеете много грязного кода try / catch повсюду, но вы все еще «защищены» от исключений, которые в противном случае прерывают ваш обычный программный поток и выливаются на уровень обработки исключений более высокого уровня код.
EDIT:
Хорошей статьей, которая охватывает предполагаемое использование исключений и почему исключения не проверяются в C #, является следующее интервью с Андерсом Хейлсбергом, главным архитектором языка C #:
http://www.artima.com/intv/handcuffsP.html
РЕДАКТИРОВАТЬ 2:
Чтобы предоставить лучший пример, который работает с кодом, который вы разместили, возможно, следующее будет более полезным. Я угадываю некоторые имена и делаю вещи одним из способов, с которыми я столкнулся при внедрении сервисов ... так что простите мне любую лицензию, которую я беру:
public PictureDataService: IPictureDataService
{
public PictureDataService(RepositoryFactory repositoryFactory, LoaderFactory loaderFactory)
{
_repositoryFactory = repositoryFactory;
_loaderFactory = loaderFactory;
}
private readonly RepositoryFactory _repositoryFactory;
private readonly LoaderFactory _loaderFactory;
private PictureDataRepository _repo;
private PictureDataLoader _loader;
public void Save(UserAccount account, UserSubmittedFile file)
{
#region Validation
if (account == null) throw new ArgumentNullException("account");
if (file == null) throw new ArgumentNullException("file");
#endregion
using (PictureDataRepository repo = getRepository())
using (PictureDataLoader loader = getLoader())
{
PictureData pictureData = loader.GetPictureData(file);
pictureData.For(account);
repo.Save(pictureData);
} // Any exceptions cause repo and loader .Dispose() methods
// to be called, cleaning up their resources...the exception
// bubbles up to the client
}
private PictureDataRepository getRepository()
{
if (_repo == null)
{
_repo = _repositoryFactory.GetPictureDataRepository();
}
return _repo;
}
private PictureDataLoader getLoader()
{
if (_loader == null)
{
_loader = _loaderFactory.GetPictureDataLoader();
}
return _loader;
}
}
public class PictureDataRepository: IDisposable
{
public PictureDataRepository(ConnectionFactory connectionFactory)
{
}
private readonly ConnectionFactory _connectionFactory;
private Connection _connection;
// ... repository implementation ...
public void Dispose()
{
GC.SuppressFinalize(this);
_connection.Close();
_connection = null; // 'detatch' from this object so GC can clean it up faster
}
}
public class PictureDataLoader: IDisposable
{
// ... Similar implementation as PictureDataRepository ...
}