Подтвердить что ты не робот

Проверьте, будет ли 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;
  }
4b9b3361

Ответ 1

Я только что столкнулся с этой же проблемой, и я использовал Reflector, чтобы посмотреть на источник для ChangeType. ChangeType генерирует исключения в 3 случаях:

  1. ConversionType является нулевым
  2. значение равно нулю
  3. значение не реализует 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.