Полный отказ от ответственности, этот код не на 100% мой код. Я вносил в него изменения на протяжении многих лет, хотя я не уверен, кому приписать основу. С учетом этого, как молодого разработчика, одним из моих самых больших страхов была необходимость преобразования строки в int, строки в DateTime или любую из тысячи с лишним комбинаций, которые могут возникнуть.
С включением Generics в C# эта проблема быстро стала простой задачей, которая меня не так сильно пугала. Я наткнулся на фрагмент кода, частично приведенный ниже, который быстро стал одной из первых вещей, которые я импортирую в любую новую программу на C#. Этот единственный фрагмент кода значительно сократил количество строк кода, которые я пишу в приложении. Иногда порядка тысячи строк кода. Позвольте мне объяснить, почему.
Скажем, я хочу преобразовать строку «1234» в int 1234. Это можно сделать разными способами. Мы можем сделать что-то вроде этого:
try { var retVal = Int32.Parse(input); } //end try catch(Exception ex) { //log the error } //end catch
Хотя это немного многословно. Для этого требуется блок try/catch, поскольку Int32.Parse терпит неудачу в менее изящном поместье.
Мы можем очистить это, выполнив следующие действия:
var retVal = 0; Int32.TryParse(input, out retVal);
Это делает код немного чище и изящно завершает работу, хотя это все еще две строки кода, что просто недостаточно. :)
Мы можем сделать это еще более ясным и лаконичным, введя статический метод, который я называю TryConvertTo, и он выглядит так:
public static T TryConvertTo<T>(this object value) { try { if ((typeof(T)) == typeof(Guid)) { return (T)Convert.ChangeType(value.ToString().TryConvertToGuid(), typeof(T)); } //end if return (T)Convert.ChangeType(value, typeof(T)); } //end try catch (Exception ex) { return default(T); } //end catch } //end TryConverTo<> public static Guid TryConvertToGuid(this object value) { try { return new Guid(value.ToString()); } //end try catch (Exception ex) { return default(Guid); } //end catch } //end TryConverToGuid
Приведенный выше фрагмент кода принимает переменную любого типа, пытается преобразовать ее в запрошенный тип и, если это не удается, возвращает тип по умолчанию для запрошенного преобразования. Нет больше проверки на null, нет необходимости оборачивать все преобразования в блоки try/catch, а результаты согласованы в том смысле, что вы получаете преобразованное значение или значение по умолчанию.
Например, если мы передадим строку «123abc» и попросим преобразовать ее в целое число, мы вернем 0, поскольку 0 является значением по умолчанию для целого числа. Это позволяет нам проверять наличие 0 в более поздних блоках после того, как происходит диалог, если мы хотим иметь расходящуюся логику для неудачных преобразований. Я предпочитаю этот метод, так как использование блока try/catch для логики маршрутизации обычно не одобряется.
Итак, как вы используете этот код? В любое время, когда вы хотите преобразовать объект в любой другой объект, вы можете просто вызвать .TryConvertTo‹T›() для базового объекта. Подробнее см. ниже.
//returns an int 123 int someInt = "123".TryConvertTo<int>(); //returns a double 123.0 double someDouble = "123".TryConvertTo<double>(); //returns an int 0 because the conversion will fail int someBadInt = "123abc".TryConvertTo<int>();
Обычно var было бы достаточно, но чтобы проиллюстрировать, что вы действительно получаете преобразованное значение, в приведенном выше коде было использовано сильное приведение типов. Использование этого статического метода позволяет быстро преобразовывать «на лету», регистрировать плохие результаты в статическом классе в согласованном поместье, легко изменять логику на основе возвращаемого значения и, что более важно, преобразовывать значения в одной строке кода.