Преобразовать строку в обнуляемый тип (int, double и т. Д.) - PullRequest
129 голосов
/ 21 апреля 2009

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

Так что у меня есть что-то вроде:

double? amount = Convert.ToDouble(strAmount);

Проблема с этим подходом состоит в том, что если strAmount пуст, если он пуст, я хочу, чтобы его значение равнялось нулю, поэтому, когда я добавлю его в базу данных, столбец будет нулевым. Итак, я написал это:

double? amount = null;
if(strAmount.Trim().Length>0)
{
    amount = Convert.ToDouble(strAmount);
}

Теперь это работает нормально, но у меня теперь пять строк кода вместо одной. Это немного затрудняет чтение, особенно когда у меня есть большое количество столбцов для конвертации.

Я подумал, что я буду использовать расширение для строкового класса и generic для передачи типа, это потому, что это может быть double, int или long. Итак, я попробовал это:

public static class GenericExtension
{
    public static Nullable<T> ConvertToNullable<T>(this string s, T type) where T: struct
    {
        if (s.Trim().Length > 0)
        {
            return (Nullable<T>)s;
        }
        return null;
    }
}

Но я получаю ошибку: невозможно преобразовать тип 'string' в 'T?'

Есть ли способ обойти это? Я не очень знаком с созданием методов с использованием обобщений.

Ответы [ 16 ]

3 голосов
/ 21 апреля 2009
public static class GenericExtension
{
    public static T? ConvertToNullable<T>(this String s) where T : struct 
    {
        try
        {
            return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
        }
        catch (Exception)
        {
            return null;
        }
    }
}
3 голосов
/ 21 апреля 2009

Обойти это невозможно. Nullable, как и ваш метод, ограничен использованием только типов значений в качестве аргумента. String является ссылочным типом и, следовательно, несовместим с этим объявлением.

2 голосов
/ 08 ноября 2017

Еще один вариант. Этот

  • Не глотает исключения
  • Выдает NotSupportedException, если тип не может быть преобразован из string. Например, пользовательская структура без конвертера типов.
  • В противном случае возвращает (T?)null, если строка не может быть проанализирована. Не нужно проверять на наличие нуля или пробелов.
using System.ComponentModel;

public static Nullable<T> ToNullable<T>(this string s) where T : struct
{
    var ret = new Nullable<T>();
    var conv = TypeDescriptor.GetConverter(typeof(T));

    if (!conv.CanConvertFrom(typeof(string)))
    {
        throw new NotSupportedException();
    }

    if (conv.IsValid(s))
    {
        ret = (T)conv.ConvertFrom(s);
    }

    return ret;
}
2 голосов
/ 09 декабря 2014

Мой пример для анонимных типов:

private object ConvertNullable(object value, Type nullableType)
{
    Type resultType = typeof(Nullable<>).MakeGenericType(nullableType.GetGenericArguments());
    return Activator.CreateInstance(resultType, Convert.ChangeType(value, nullableType.GetGenericArguments()[0]));
}

...

Type anonimousType = typeof(Nullable<int>);
object nullableInt1 = ConvertNullable("5", anonimousType);
// or evident Type
Nullable<int> nullableInt2 = (Nullable<int>)ConvertNullable("5", typeof(Nullable<int>));
0 голосов
/ 01 августа 2018

Общий ответ, предоставленный " Джоэл Кохорн ", хорош.

Но это другой способ без использования этих GetConverter... или try/catch блоков ... (я не уверен, но в некоторых случаях это может иметь лучшую производительность):

public static class StrToNumberExtensions
{
    public static short ToShort(this string s, short defaultValue = 0) => short.TryParse(s, out var v) ? v : defaultValue;
    public static int ToInt(this string s, int defaultValue = 0) => int.TryParse(s, out var v) ? v : defaultValue;
    public static long ToLong(this string s, long defaultValue = 0) => long.TryParse(s, out var v) ? v : defaultValue;
    public static decimal ToDecimal(this string s, decimal defaultValue = 0) => decimal.TryParse(s, out var v) ? v : defaultValue;
    public static float ToFloat(this string s, float defaultValue = 0) => float.TryParse(s, out var v) ? v : defaultValue;
    public static double ToDouble(this string s, double defaultValue = 0) => double.TryParse(s, out var v) ? v : defaultValue;

    public static short? ToshortNullable(this string s, short? defaultValue = null) => short.TryParse(s, out var v) ? v : defaultValue;
    public static int? ToIntNullable(this string s, int? defaultValue = null) => int.TryParse(s, out var v) ? v : defaultValue;
    public static long? ToLongNullable(this string s, long? defaultValue = null) => long.TryParse(s, out var v) ? v : defaultValue;
    public static decimal? ToDecimalNullable(this string s, decimal? defaultValue = null) => decimal.TryParse(s, out var v) ? v : defaultValue;
    public static float? ToFloatNullable(this string s, float? defaultValue = null) => float.TryParse(s, out var v) ? v : defaultValue;
    public static double? ToDoubleNullable(this string s, double? defaultValue = null) => double.TryParse(s, out var v) ? v : defaultValue;
}

Использование выглядит следующим образом:

var x1 = "123".ToInt(); //123
var x2 = "abc".ToInt(); //0
var x3 = "abc".ToIntNullable(); // (int?)null 
int x4 = ((string)null).ToInt(-1); // -1
int x5 = "abc".ToInt(-1); // -1

var y = "19.50".ToDecimal(); //19.50

var z1 = "invalid number string".ToDoubleNullable(); // (double?)null
var z2 = "invalid number string".ToDoubleNullable(0); // (double?)0
0 голосов
/ 24 февраля 2017

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

/// <summary>
    /// <para>More convenient than using T.TryParse(string, out T). 
    /// Works with primitive types, structs, and enums.
    /// Tries to parse the string to an instance of the type specified.
    /// If the input cannot be parsed, null will be returned.
    /// </para>
    /// <para>
    /// If the value of the caller is null, null will be returned.
    /// So if you have "string s = null;" and then you try "s.ToNullable...",
    /// null will be returned. No null exception will be thrown. 
    /// </para>
    /// <author>Contributed by Taylor Love (Pangamma)</author>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="p_self"></param>
    /// <returns></returns>
    public static T? ToNullable<T>(this string p_self) where T : struct
    {
        if (!string.IsNullOrEmpty(p_self))
        {
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
            if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
            if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
        }

        return null;
    }

https://github.com/Pangamma/PangammaUtilities-CSharp/blob/master/PangammaUtilities/Extensions/ToNullableStringExtension.cs

...