Как мне сделать универсальный метод преобразования типов - PullRequest
2 голосов
/ 17 января 2009

Что я хочу сделать, это:

bool Convert( out Object output, Object source)
{
   // find type of output.
   // convert source to that type if possible
   // store result in output.

   return success
} 

Возможно ли это?

Очевидно, что существует массивная конструкция "если" грубой силы, которая может сработать, но для этого потребуется написать блок if для каждого мыслимого типа данных. Даже если предположить, что мы ограничим его примитивами и строками, это все еще огромный кусок кода. Я думаю о чем-то более размышляющем.

В сторону: Проходя через API, я столкнулся с методом Convert.IsDBNull (), который сэкономит мне много

 if ( !databasefield.GetType().Equals( DBNull.Value ) )

Почему во имя Б-га это в Конверт? Почему бы не DBNull.IsDBNull ()?

Ответы [ 9 ]

3 голосов
/ 17 января 2009

Один из разработчиков написал именно эту функцию, что мы считаем чрезвычайно полезным.

По сути, он использует отражение для поиска неявного преобразования между двумя типами (для получения дополнительной информации ищите "op_Implicit").

В противном случае он ищет Конструктор типа назначения, который принимает тип источника в качестве параметра и вызывает его.

В противном случае он ищет метод Parse, который может анализировать один тип в другой. Здесь вы найдете такие вещи, как Int32.Parse для преобразования из String в Int или IPAddress.Parse для преобразования из String в IPAddress.

В качестве оптимизации производительности, когда он находит преобразование один раз, он хранит его в словаре [тип, тип] <==> [метод преобразования информации], чтобы последующим вызовам не приходилось проходить расширенный поиск отражения. .

Это прекрасно обрабатывает почти все преобразования типов.

3 голосов
/ 17 января 2009

Вот пример, который я использую, вы можете добавить в него другие сложные преобразования, зарегистрировав преобразователи других типов.

public static class Converter
{
    public static T Convert<T>(object obj, T defaultValue)
    {
        if (obj != null)
        {
            if (obj is T)
            {
                return (T)obj;
            }

            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));

            if (converter.CanConvertFrom(obj.GetType()))
            {
                return (T)converter.ConvertFrom(obj);
            }
        }

        return defaultValue;
    }
2 голосов
/ 17 января 2009

Это было забавное маленькое упражнение! Я только что написал это, так что не вешайте меня, если это не работает, но здесь я пытаюсь конвертировать с помощью различных методов, которые я могу придумать сейчас.

public static class Converter
{
    public static bool TryConvert<T>(object o, out T result)
    {
        if (o == null && typeof(T).IsClass)
        {
            result = default(T);
            return true;
        }

        var convertible = o as IConvertible;
        if (convertible != null && ConvertibleHandlesDestinationType<T>())
        {
            result = (T)Convert.ChangeType(convertible, typeof(T));
            return true;
        }

        if (o != null)
        {
            if (typeof(T).IsAssignableFrom(o.GetType()))
            {
                result = (T)o;
                return true;
            }

            var converter = TypeDescriptor.GetConverter(o);
            if (converter.CanConvertTo(typeof(T)))
            {
                result = (T)converter.ConvertTo(o, typeof(T));
                return true;
            }
        }

        result = default(T);
        return false;
    }

    private static bool ConvertibleHandlesDestinationType<T>()
    {
        return 
            typeof(T).Equals(typeof(Boolean)) ||
            typeof(T).Equals(typeof(Byte)) ||
            typeof(T).Equals(typeof(char)) ||
            typeof(T).Equals(typeof(DateTime)) ||
            typeof(T).Equals(typeof(Decimal)) ||
            typeof(T).Equals(typeof(Double)) ||
            typeof(T).Equals(typeof(Int16)) ||
            typeof(T).Equals(typeof(Int32)) ||
            typeof(T).Equals(typeof(Int64)) ||
            typeof(T).Equals(typeof(SByte)) ||
            typeof(T).Equals(typeof(Single)) ||
            typeof(T).Equals(typeof(string)) ||
            typeof(T).Equals(typeof(UInt16)) ||
            typeof(T).Equals(typeof(UInt32)) ||
            typeof(T).Equals(typeof(UInt64));
    }
}

Поскольку выходной параметр имеет тип T, мы можем использовать вывод типа, чтобы вызовы выглядели так:

int number;
if (Converter.TryConvert("123", out number))
{
    Debug.WriteLine(number);
}
2 голосов
/ 17 января 2009

Я много раз сталкивался с вашим вопросом. Я всегда обнаруживал, что время на создание и использование функции преобразования сводит на нет экономию времени. В конечном итоге возникают такие проблемы, как точность и округление, и вам все равно приходится обрабатывать особые случаи.

Для проверки на dbnull ... Я использую typeof (объект) - это DbNull ...

1 голос
/ 17 января 2009

Даже с Convert.IsDBNull, есть гораздо лучший способ сделать ваш чек:

if (!databaseField is DBNull)

Также помните, что вы можете использовать == для Типа, поскольку для определенного типа существует только один экземпляр Type.

0 голосов
/ 18 января 2009

Нет Святого Грааля обращения. Для m типов вам потребуются m * (m-1) процедуры преобразования для покрытия всех перестановок.

Для примитивных типов используйте Convert.ChangeType

Если тип может быть преобразован в примитив, он может реализовать интерфейс IConvertable и использоваться из класса Convert.

Для всего остального, ответ @Brian Rudolfs самый лучший. Зарегистрируйте явный метод преобразования для каждой требуемой перестановки.

0 голосов
/ 18 января 2009

В своем блоге я написал сообщение о том, как управлять преобразованием типов из DataRow Работа с таблицами данных / Datarows

///<summary>
/// Extension methods for manipulating DataRows
///</summary>
public static class DataRowUserExtensions
{
    /// <summary>
    /// Determines whether [is null or empty string] [the specified data row].
    /// </summary>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns>
    ///   <c>true</c> if [is null or empty string] [the specified data row]; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNullOrEmptyString(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return dataRow[key] == null || dataRow[key] == DBNull.Value || dataRow[key].ToString() == string.Empty;

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Gets the specified data row.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public static T Get<T>(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return dataRow.IsNullOrEmptyString(key) ? default(T) : (T) ChangeTypeTo<T>(dataRow[key]);

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Changes the type to.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    private static object ChangeTypeTo<T>(this object value)
    {
        if (value == null)
            return null;

        Type underlyingType = typeof (T);
        if (underlyingType == null)
            throw new ArgumentNullException("value");

        if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition().Equals(typeof (Nullable<>)))
        {
            var converter = new NullableConverter(underlyingType);
            underlyingType = converter.UnderlyingType;
        }

        // Guid convert
        if (underlyingType == typeof (Guid))
        {
            return new Guid(value.ToString());
        }

        // Do conversion
        return underlyingType.IsAssignableFrom(value.GetType()) ?
              Convert.ChangeType(value, underlyingType)
            : Convert.ChangeType(value.ToString(), underlyingType);
    }
}
0 голосов
/ 17 января 2009

Вы видели эту функцию?:

Microsoft.VisualBasic.CType()
0 голосов
/ 17 января 2009

Попробуйте использовать дженерики. Таким образом, вам не нужно выполнять всю проверку типов во время выполнения, как в противном случае (все вместо этого выполняется во время компиляции).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...