Я пытаюсь улучшить обработку исключений, но мне кажется, что мой код становится очень уродливым, нечитаемым и загроможденным, когда я стараюсь их поймать. Мне бы хотелось увидеть, как к этому подходят другие люди, давая практический пример и сравнивая решения.
Мой примерный метод загружает данные из URL-адреса и пытается сериализовать его в заданный тип, а затем возвращает экземпляр, заполненный данными.
Во-первых, без каких-либо обработки исключений:
private static T LoadAndSerialize<T>(string url)
{
var uri = new Uri(url);
var request = WebRequest.Create(uri);
var response = request.GetResponse();
var stream = response.GetResponseStream();
var result = Activator.CreateInstance<T>();
var serializer = new DataContractJsonSerializer(result.GetType());
return (T)serializer.ReadObject(stream);
}
Мне кажется, что этот метод достаточно читабельен. Я знаю, что есть несколько ненужных шагов в методе (например, WebRequest.Create() может принимать строку, и я мог бы цепочки методов без предоставления им переменных), но я оставлю это как это, чтобы лучше сравнить с версией с исключением- обработка.
Это первая попытка обработать все, что может пойти не так:
private static T LoadAndSerialize<T>(string url)
{
Uri uri;
WebRequest request;
WebResponse response;
Stream stream;
T instance;
DataContractJsonSerializer serializer;
try
{
uri = new Uri(url);
}
catch (Exception e)
{
throw new Exception("LoadAndSerialize : Parameter 'url' is malformed or missing.", e);
}
try
{
request = WebRequest.Create(uri);
}
catch (Exception e)
{
throw new Exception("LoadAndSerialize : Unable to create WebRequest.", e);
}
try
{
response = request.GetResponse();
}
catch (Exception e)
{
throw new Exception(string.Format("LoadAndSerialize : Error while getting response from host '{0}'.", uri.Host), e);
}
if (response == null) throw new Exception(string.Format("LoadAndSerialize : No response from host '{0}'.", uri.Host));
try
{
stream = response.GetResponseStream();
}
catch (Exception e)
{
throw new Exception("LoadAndSerialize : Unable to get stream from response.", e);
}
if (stream == null) throw new Exception("LoadAndSerialize : Unable to get a stream from response.");
try
{
instance = Activator.CreateInstance<T>();
}
catch (Exception e)
{
throw new Exception(string.Format("LoadAndSerialize : Unable to create and instance of '{0}' (no parameterless constructor?).", typeof(T).Name), e);
}
try
{
serializer = new DataContractJsonSerializer(instance.GetType());
}
catch (Exception e)
{
throw new Exception(string.Format("LoadAndSerialize : Unable to create serializer for '{0}' (databinding issues?).", typeof(T).Name), e);
}
try
{
instance = (T)serializer.ReadObject(stream);
}
catch (Exception e)
{
throw new Exception(string.Format("LoadAndSerialize : Unable to serialize stream into '{0}'.", typeof(T).Name), e);
}
return instance;
}
Проблема здесь в том, что, хотя все, что может пойти не так, будет поймано и дано несколько значимое исключение, это беспорядок-фестиваль значительных размеров.
Итак, что, если я зацеплю за ловушку. Моя следующая попытка:
private static T LoadAndSerialize<T>(string url)
{
try
{
var uri = new Uri(url);
var request = WebRequest.Create(uri);
var response = request.GetResponse();
var stream = response.GetResponseStream();
var serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(stream);
}
catch (ArgumentNullException e)
{
throw new Exception("LoadAndSerialize : Parameter 'url' cannot be null.", e);
}
catch (UriFormatException e)
{
throw new Exception("LoadAndSerialize : Parameter 'url' is malformed.", e);
}
catch (NotSupportedException e)
{
throw new Exception("LoadAndSerialize : Unable to create WebRequest or get response stream, operation not supported.", e);
}
catch (System.Security.SecurityException e)
{
throw new Exception("LoadAndSerialize : Unable to create WebRequest, operation was prohibited.", e);
}
catch (NotImplementedException e)
{
throw new Exception("LoadAndSerialize : Unable to get response from WebRequest, method not implemented?!.", e);
}
catch(NullReferenceException e)
{
throw new Exception("LoadAndSerialize : Response or stream was empty.", e);
}
}
В то время как это конечно легче на глазах, я сильно наклоняюсь к intellisense, чтобы предоставить все исключения, которые могут быть выброшены из метода или класса. Я не уверен, что эта документация на 100% точна, и будет еще более скептически, если некоторые из методов приходят из сборки вне рамок .net. В качестве примера в DataContractJsonSerializer нет исключений для intellisense. Означает ли это, что конструктор никогда не потерпит неудачу? Могу ли я быть уверенным?
Другие проблемы, связанные с этим, заключаются в том, что некоторые из методов генерируют одно и то же исключение, что затрудняет описание ошибки (что-то или нет) и поэтому менее полезно для пользователя/отладчика.
Третий вариант должен был бы игнорировать все исключения, кроме тех, которые позволили бы мне предпринять действие подобно повторному соединению. Если url имеет значение null, url имеет значение null, единственное преимущество от catching - это немного более подробное сообщение об ошибке.
Мне бы хотелось увидеть ваши мысли и/или реализации!