Преобразовать строку в обнуляемый тип (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 ]

150 голосов
/ 21 апреля 2009

Следует также помнить, что сама строка может быть нулевой.

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    Nullable<T> result = new Nullable<T>();
    try
    {
        if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
        {
            TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
            result = (T)conv.ConvertFrom(s);
        }
    }
    catch { } 
    return result;
}
49 голосов
/ 21 апреля 2009

Вы можете попробовать использовать метод расширения ниже:

public static T? GetValueOrNull<T>(this string valueAsString)
    where T : struct 
{
    if (string.IsNullOrEmpty(valueAsString))
        return null;
    return (T) Convert.ChangeType(valueAsString, typeof(T));
}

Таким образом, вы можете сделать это:

double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();
24 голосов
/ 21 апреля 2009

Как насчет этого:

<code>
double? amount = string.IsNullOrEmpty(strAmount) ? (double?)null : Convert.ToDouble(strAmount);

Конечно, это не учитывает ошибку преобразования.

22 голосов
/ 02 июня 2009

Я написал этот конвертер универсального типа. Он работает с Nullable и стандартными значениями, конвертируя все конвертируемые типы - не только строки. Он обрабатывает все виды сценариев, которые вы ожидаете (значения по умолчанию, нулевые значения, другие значения и т. Д ...)

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

    public static T To<T>(this IConvertible obj)
    {
        Type t = typeof(T);

        if (t.IsGenericType
            && (t.GetGenericTypeDefinition() == typeof(Nullable<>)))
        {
            if (obj == null)
            {
                return (T)(object)null;
            }
            else
            {
                return (T)Convert.ChangeType(obj, Nullable.GetUnderlyingType(t));
            }
        }
        else
        {
            return (T)Convert.ChangeType(obj, t);
        }
    }

    public static T ToOrDefault<T>
                 (this IConvertible obj)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return default(T);
        }
    }

    public static bool ToOrDefault<T>
                        (this IConvertible obj,
                         out T newObj)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = default(T);
            return false;
        }
    }

    public static T ToOrOther<T>
                           (this IConvertible obj,
                           T other)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return other;
        }
    }

    public static bool ToOrOther<T>
                             (this IConvertible obj,
                             out T newObj,
                             T other)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = other;
            return false;
        }
    }

    public static T ToOrNull<T>
                          (this IConvertible obj)
                          where T : class
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return null;
        }
    }

    public static bool ToOrNull<T>
                      (this IConvertible obj,
                      out T newObj)
                      where T : class
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = null;
            return false;
        }
    }
9 голосов
/ 21 апреля 2009

Возможно, вы захотите попробовать:

TypeConverter conv = TypeDescriptor.GetConverter(typeof(int));
conv.ConvertFrom(mystring);

сделайте свою собственную проверку на ноль и верните int? при необходимости. Вы также захотите обернуть это в try {}

6 голосов
/ 21 апреля 2009

Дайте этому шанс ...

public delegate bool TryParseDelegate<T>(string data, out T output);

public static T? ToNullablePrimitive<T>(this string data, 
    TryParseDelegate<T> func) where T:struct
{
    string.IsNullOrEmpty(data) return null;

    T output;

    if (func(data, out output))
    {
        return (T?)output;
    }

    return null;
}

Тогда называй это так ...

void doStuff()
{
    string foo = "1.0";

    double? myDouble = foo.ToNullablePrimitive<double>(double.TryParse);

    foo = "1";

    int? myInt = foo.ToNullablePrimitive<int>(int.TryParse);

    foo = "haha";

    int? myInt2 = foo.ToNullablePrimitive<int>(int.TryParse);
}
5 голосов
/ 06 июля 2012

Вы можете использовать следующее с объектами, к сожалению, это не работает со строками.

double? amount = (double?)someObject;

Я использую его для переноса переменной сеанса в свойство (на базовой странице) .. поэтому мое фактическое использование (на моей базовой странице):

public int? OrganisationID
{
    get { return (int?)Session[Constants.Session_Key_OrganisationID]; }
    set { Session[Constants.Session_Key_OrganisationID] = value; }
}

Я могу проверить ноль в логике страницы:

if (base.OrganisationID == null)
    // do stuff
5 голосов
/ 02 февраля 2011

Мне нравится ответ Джоэла, но я немного изменил его, поскольку я не фанат употребления исключений.

    /// <summary>
    /// Converts a string to the specified nullable type.
    /// </summary>
    /// <typeparam name="T">The type to convert to</typeparam>
    /// <param name="s">The string to convert</param>
    /// <returns>The nullable output</returns>
    public static T? ToNullable<T>(this string s) where T : struct
    {
        if (string.IsNullOrWhiteSpace(s))
            return null;

        TypeConverter conv = TypeDescriptor.GetConverter(typeof (T));
        return (T) conv.ConvertFrom(s);
    }

    /// <summary>
    /// Attempts to convert a string to the specified nullable primative.
    /// </summary>
    /// <typeparam name="T">The primitive type to convert to</typeparam>
    /// <param name="data">The string to convert</param>
    /// <param name="output">The nullable output</param>
    /// <returns>
    /// True if conversion is successfull, false otherwise.  Null and whitespace will
    /// be converted to null and return true.
    /// </returns>
    public static bool TryParseNullable<T>(this string data, out T? output) where T : struct
    {
        try
        {
            output = data.ToNullable<T>();
            return true;
        }
        catch
        {
            output = null;
            return false;
        }
    }
3 голосов
/ 04 мая 2016

Вот что-то, основанное на принятом ответе. Я удалил try / catch, чтобы убедиться, что все исключения не проглочены и не обработаны. Также убедитесь, что возвращаемая переменная (в принятом ответе) никогда не инициализируется дважды даром.

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    if (!string.IsNullOrWhiteSpace(s))
    {
        TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));

        return (T)conv.ConvertFrom(s);
    }

    return default(Nullable<T>);
}
3 голосов
/ 10 ноября 2011

Существует универсальное решение (для любого типа). Юзабилити хороша, но реализация должна быть улучшена: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Это позволяет вам написать очень чистый код, подобный этому:

string value = null;
int? x = value.ConvertOrDefault<int?>();

, а также:

object obj = 1;  

string value = null;
int x = 5;
if (value.TryConvert(out x))
    Console.WriteLine("TryConvert example: " + x); 

bool boolean = "false".ConvertOrDefault<bool>();
bool? nullableBoolean = "".ConvertOrDefault<bool?>();
int integer = obj.ConvertOrDefault<int>();
int negativeInteger = "-12123".ConvertOrDefault<int>();
int? nullableInteger = value.ConvertOrDefault<int?>();
MyEnum enumValue = "SecondValue".ConvertOrDefault<MyEnum>();

MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault<MyObjectClassA>();
...