Метод расширение для безопасного приведения типов
- modified:
- reading: 3 minutes
В добавление к записи Дмитрия Нестерука - Паттерны методов расширения хотел бы добавить еще один метод расширение “Приведение типов”, без которого мне уже сложно обходиться. Автор идеи этого подхода работает сейчас в фирме, где я работал раньше.
Нам часто приходится писать, примерно, такой код:
int intValue;
if (obj == null || !int.TryParse(obj.ToString(), out intValue))
intValue = 0;
Это способ безопасного приведения к типу int. Напрашивается сразу же какой либо унифицированный метод для безопасного приведения типов.
Мне нравится подход вынесения преобразования в extension method и использовать его затем следующим образом:
int i;
i = "1".To<int>();
// i == 1
i = "1a".To<int>();
// i == 0 (default value of int)
i = "1a".To(10);
// i == 10 (set as default value 10)
i = "1".To(10);
// i == 1
// ********** Nullable sample **************
int? j;
j = "1".To<int?>();
// j == 1
j = "1a".To<int?>();
// j == null
j = "1a".To<int?>(10);
// j == 10
j = "1".To<int?>(10);
// j == 1
И соответственно реализация данного подхода:
public static class Parser
{ /// <summary>
/// Try cast <paramref name="obj"/> value to type <typeparamref name="T"/>,
/// if can't will return default(<typeparamref name="T"/>)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T To<T>(this object obj)
{ return To(obj, default(T));
}
/// <summary>
/// Try cast <paramref name="obj"/> value to type <typeparamref name="T"/>,
/// if can't will return <paramref name="defaultValue"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public static T To<T>(this object obj, T defaultValue)
{ if (obj == null)
return defaultValue;
if (obj is T)
return (T) obj;
Type type = typeof (T);
// Place convert to reference types here
if (type == typeof(string))
{ return (T)(object)obj.ToString();
}
Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null)
{ return To(obj, defaultValue, underlyingType);
}
return To(obj, defaultValue, type);
}
private static T To<T>(object obj, T defaultValue, Type type)
{ // Place convert to sructures types here
if (type == typeof(int))
{ int intValue;
if (int.TryParse(obj.ToString(), out intValue))
return (T)(object)intValue;
return defaultValue;
}
if (type == typeof(long))
{ long intValue;
if (long.TryParse(obj.ToString(), out intValue))
return (T)(object)intValue;
return defaultValue;
}
if (type == typeof(bool))
{ bool bValue;
if (bool.TryParse(obj.ToString(), out bValue))
return (T)(object)bValue;
return defaultValue;
}
if (type.IsEnum)
{ if (Enum.IsDefined(type, obj))
return (T)Enum.Parse(type, obj.ToString());
return defaultValue;
}
throw new NotSupportedException(string.Format("Couldn't parse to Type {0}", typeof(T)));
}
}
Данный метод не полон – это уже моя собственная реализация данного подхода, которую я использую внутри своего сайта. Он умеет работать с int, long, bool, string, enums (и с Nullable типами этих типов), но, думаю, по ходу работы вам не составит труда дополнять данный парсер необходимыми типами (главное не забывайте про культуры при необходимости).
Очевидно, что данный подход хорошо использовать только внутри знающих что делает данный метод разработчиков, так как не совсем очевидно, почему этот метод не может преобразовать любой тип A к типу B.