Проверьте, будет ли Convert.ChangeType работать между двумя типами
Это продолжение этого вопроса о преобразовании значений с отражением. Преобразование объекта определенного типа в другой тип может быть выполнено следующим образом:
object convertedValue = Convert.ChangeType(value, targetType);
Учитывая два экземпляра типа (например, FromType и ToType), есть ли способ проверить, будет ли преобразование успешным?
например. могу ли я написать метод расширения следующим образом:
public static class TypeExtensions
{
public static bool CanChangeType(this Type fromType, Type toType)
{
// what to put here?
}
}
EDIT: Это то, что я имею прямо сейчас. Уродливо, но я еще не вижу другого пути...
bool CanChangeType(Type sourceType, Type targetType)
{
try
{
var instanceOfSourceType = Activator.CreateInstance(sourceType);
Convert.ChangeType(instanceOfSourceType, targetType);
return true; // OK, it can be converted
}
catch (Exception ex)
{
return false;
}
Ответы
Ответ 1
Я только что столкнулся с этой же проблемой, и я использовал Reflector, чтобы посмотреть на источник для ChangeType. ChangeType генерирует исключения в 3 случаях:
- ConversionType является нулевым
- значение равно нулю
- значение не реализует IConvertible
После того, как эти 3 проверены, это гарантирует, что это может быть преобразовано. Таким образом, вы можете сохранить большую производительность и удалить блок try {}/catch {}, просто проверив эти 3 вещи самостоятельно:
public static bool CanChangeType(object value, Type conversionType)
{
if (conversionType == null)
{
return false;
}
if (value == null)
{
return false;
}
IConvertible convertible = value as IConvertible;
if (convertible == null)
{
return false;
}
return true;
}
Ответ 2
Проверка метода Convert.ChangeType в отражателе Я нашел это в статическом конструкторе:
ConvertTypes = new Type[] {
typeof(Empty), typeof(object), typeof(DBNull), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal),
typeof(DateTime), typeof(object), typeof(string)
};
В конце концов, этот метод просто проверяет либо, если источник реализует IConvertible, либо цель является одним из вышеперечисленных типов. Поэтому ваш метод должен выглядеть примерно так (очень грубо):
return (ConvertTypes.Contains(toType) || typeof(IConvertible).IsAssignableFrom(fromType));
Ответ 3
Я написал небольшую структуру, которая включает класс Convert, который может делать больше, чем класс System.Convert. Если вы заинтересованы в его использовании, вы можете скачать его с Github. Он не может определить, можете ли вы конвертировать значения, но это хорошая возможность для добавления.
Он включает в себя возможность конвертировать значения на основе:
- IConvertible
- TypeConverters
- Методы ToXxx
- Разбор статических методов
- Параметризованный конструктор
- и несколько других второстепенных
Преобразование типов данных
Ответ 4
Основываясь на том, что уже ответили на оба вопроса, я придумал это (требуется С# 7)
public static object ChangeType(object value, Type conversion)
{
var type = conversion;
if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
type = Nullable.GetUnderlyingType(type);
}
return Convert.ChangeType(value, type);
}
public static (bool IsSuccess, object Value) TryChangeType(object value, Type conversionType)
{
(bool IsSuccess, object Value) response = (false, null);
var isNotConvertible =
conversionType == null
|| value == null
|| !(value is IConvertible)
|| !(value.GetType() == conversionType);
if (isNotConvertible)
{
return response;
}
try
{
response = (true, ChangeType(value, conversionType));
}
catch (Exception)
{
response.Value = null;
}
return response;
}
}
Пример производственного кода:
public Item()
{
foreach (var pinfo in GetType().GetProperties())
{
object value = 0;
var response = ReflectionHelpers.TryChangeType(value, pinfo.PropertyType);
if(response.IsSuccess)
{
pinfo.SetValue(this, response.Value);
}
}
}
Это позволяет все возможные свойства с помощью 0.