문자열을 널 입력 가능 유형으로 변환 (int, double 등)


137

데이터 변환을 시도하고 있습니다. 불행히도, 대부분의 데이터는 int 또는 double이어야하는 문자열에 있습니다 ...

내가 가진 것은 다음과 같습니다.

double? amount = Convert.ToDouble(strAmount);

이 접근 방식의 문제점은 strAmount가 비어 있으면 비어 있으면 null이되기를 원하므로 데이터베이스에 추가하면 열이 null입니다. 그래서 나는 이것을 작성했다 :

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'로 변환 할 수 없습니까?

이 주위에 방법이 있습니까? 제네릭을 사용하여 메서드를 만드는 데 익숙하지 않습니다.


답변:


157

명심해야 할 또 다른 사항은 문자열 자체가 null 일 수 있다는 것입니다.

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;
}

2
"T type"매개 변수는 사용되지 않으므로 생략 할 수 있습니다.
Michael Meadows

1
+1, 그냥 이길 수 있습니다. 작은 이쑤시개 : 변환 된 값은 결과가 아닌 결과에 직접 할당되어야합니다. 즉, "result = (T) conv.ConvertFrom (s);"입니다.
LukeH 2009

20
당신이 .Net4 사용하는 경우이 string.IsNullOrWhiteSpace ()을 조금 단순화 할 수있다
세르게이 Andrejev

1
@andrefadila-사용 방법 : string sampleVendorId = ""; INT? vendorId = sampleVendorId.ToNullable <int> ();
minerva

1
conv.ConvertFrom 호출은 널 입력 가능 유형의 T를 변환하지 않으므로이 기능을 약간 직관적으로 만들 수 있습니다. 이 기능을 시도 할 필요는 없습니다. 이 세 줄의 코드로 모든 것을 만들 수 있습니다 : if (string.IsNullOrWhiteSpace (stringObject)) 반환 null; var conv = TypeDescriptor.GetConverter (typeof (T)); return (T?) conv.ConvertFrom (stringObject);
David

54

아래 확장 방법을 사용해보십시오.

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>();

3
IMHO 이것은 문제에 대한 가장 우아한 해결책입니다
Zaffiro

4
실제로 ..이 솔루션은 작동하지 않습니다. changetype은 널 입력 가능 유형으로 변환되지 않습니다. 대신 typeconverter를 사용하십시오
AaronHS

이것이 내가 알아야 할 것입니다 ... Convert.ChangeType-Method를 사용할 때 Nullable-Type의 기본 유형을 사용해야합니다. 매개 변수 conversionType에 대해 Nullable-Typ과 함께 작동하지 않기 때문입니다.
Marcus.D

27

이건 어때?


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

물론 이것은 변환 실패를 고려하지 않습니다.


반환 값 중 하나를 이중으로 캐스팅하면? (또는 int? 등) 그러면 최종 double?로 변환 할 수 있습니다. 위의 변경 사항을 참조하십시오.
bdukes 2009

미안합니다. 컴파일러가 소리를 지르기 전까지는 항상 캐스트를 잊어 버리십시오. :)
John Kraft

null이 아니고 amount.HasValue를 시도하고 amount를 var로 선언하면 실패합니다.
Steve

23

이 제네릭 형식 변환기를 작성했습니다. Nullable 및 표준 값과 함께 작동하며 문자열뿐만 아니라 모든 컨버터블 유형 간을 변환합니다. 예상되는 모든 종류의 시나리오 (기본값, null 값, 기타 값 등)를 처리합니다.

나는 이것을 수십 개의 프로덕션 프로그램에서 약 1 년 동안 사용해 왔으므로 꽤 견고해야합니다.

    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;
        }
    }

2
모든 전환 오류를 무시하는 것이 옳은 것이라고 생각하지 않습니다. 또한 모든 종류의 예외를 삼켜서는 안됩니다. OutOfMemoryException고정 된 예외 유형으로 범위를 좁힐 수 없으면 최소한 다시 던지 십시오.
Paul Groke

9

시도해 볼 수 있습니다.

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

자신의 null 검사를 수행하고 int?필요한 경우 반환 하십시오. 당신은 또한 그것을try {}


6

이 기회를 ...

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);
}

6

나는 Joel의 대답을 좋아하지만 예외를 먹는 팬이 아니기 때문에 약간 수정했습니다.

    /// <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;
        }
    }

5

불행히도 문자열과 함께 작동하지는 않지만 객체와 함께 다음을 사용할 수 있습니다.

double? amount = (double?)someObject;

세션 변수를 속성 (기본 페이지)에 래핑하는 데 사용합니다. 따라서 실제 사용법은 (기본 페이지)입니다.

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

페이지 로직에서 null을 확인할 수 있습니다.

if (base.OrganisationID == null)
    // do stuff

안녕하세요,이 문제가 해결되었습니다. 참고로 나는 VB.NET을 사용하였고, VB의 equivilant은 CType(Object, Nullable(Of Double))문자열 잘 작동
rayzinnz

문자열과 함께 사용할 수있는 첫 번째 예제 버전이 있습니까?
wazz

3

이 문제를 해결할 방법이 없습니다. Nullable과 메서드는 인수로서 값 형식 만 사용하도록 제한됩니다. 문자열은 참조 유형이므로이 선언과 호환되지 않습니다.


3
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

일반적인 솔루션이 있습니다 (모든 유형). 유용성은 좋지만 구현은 개선되어야합니다. 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>();

downvoting 누가이 보편적 인 해결책에 문제가있는 코멘트를 추가하십시오.
Pavel Hodek 8

1
글쎄, 먼저 당신의 대답에 매우 잘못된 것이 있습니다. 그리고 그것은 "다른 모든 대답을 잊을 수 있습니다"입니다. 그것이 사실이더라도 잘못 될 것입니다 (그렇지 않습니다). 그리고 "유니버설 솔루션"의 문제점은 성능이 좋지 않고 ( typeName.IndexOf? 정말로?) 스트롱 동작으로 가득 차 있다는 것입니다 (표시된 TryConvert함수는 null 값을 올바르게 처리하지 않습니다).
Paul Groke

3

다음은 허용되는 답변을 바탕으로 한 것입니다. 모든 예외가 삼키지 않고 처리되지 않도록 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>);
}

2

익명 유형에 대한 나의 예 :

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>));

2

또 다른 변형. 이 하나

  • 예외를 삼키지 않습니다
  • NotSupportedException형식을에서 변환 할 수없는 경우 a를 throw합니다 string. 예를 들어, 유형 변환기가없는 사용자 정의 구조체.
  • 그렇지 않으면 (T?)null문자열을 구문 분석하지 못하면 a를 반환합니다 . 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;
}

1

스택에 하나 더 유사한 솔루션을 추가합시다. 이것은 또한 열거 형을 구문 분석하며 멋지게 보입니다. 매우 안전.

/// <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


0

" Joel Coehoorn "이 제공하는 일반적인 답변 이 좋습니다.

그러나 이것은 블록 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

@MassimilianoKraus는있을 수 있지만 한 번만 작성되었지만 항상 사용하는 간단한 12 줄 코드입니다. 그리고 내가 말했듯이, TypeDescriptor.GetConverter... 코드를 사용하는 것보다 빠를 수도 있습니다 . 이것은 또 다른 방법입니다.
S.Serpooshan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.