แปลงสตริงเป็นชนิด nullable (int, double, etc …)


137

ฉันพยายามแปลงข้อมูลบางอย่าง น่าเสียดายที่ข้อมูลส่วนใหญ่อยู่ในสตริงซึ่งควรเป็น int หรือ double เป็นต้น

ดังนั้นสิ่งที่ฉันมีคือ:

double? amount = Convert.ToDouble(strAmount);

ปัญหาของวิธีนี้คือถ้า strAmount ว่างเปล่าถ้ามันว่างเปล่าฉันต้องการให้มันเป็นจำนวนว่างดังนั้นเมื่อฉันเพิ่มลงในฐานข้อมูลคอลัมน์จะเป็นโมฆะ ดังนั้นฉันจึงลงเอยด้วยการเขียนสิ่งนี้:

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

ตอนนี้ใช้ได้ดี แต่ตอนนี้ฉันมีโค้ดห้าบรรทัดแทนที่จะเป็นหนึ่งบรรทัด สิ่งนี้ทำให้อ่านยากขึ้นเล็กน้อยโดยเฉพาะเมื่อฉันมีคอลัมน์จำนวนมากที่ต้องแปลง

ฉันคิดว่าฉันจะใช้ส่วนขยายของคลาสสตริงและของทั่วไปเพื่อส่งเป็นประเภทเพราะนี่อาจเป็น double หรือ int หรือยาวก็ได้ ดังนั้นฉันจึงลองทำสิ่งนี้:

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

สิ่งที่ควรทราบก็คือสตริงตัวเองอาจเป็นโมฆะ

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" ได้เนื่องจากไม่ได้ใช้
46432 Michael Meadows เมื่อ

1
+1, เอาชนะฉันให้ได้ nitpick ขนาดเล็ก: ค่าที่แปลงแล้วจะต้องกำหนดโดยตรงกับผลลัพธ์ไม่ใช่เพื่อผลลัพธ์ เช่น "result = (T) conv.ConvertFrom (s);"
LukeH

20
สิ่งนี้สามารถทำให้ง่ายขึ้นเล็กน้อยด้วยสตริง IsNullOrWhiteSpace () ถ้าคุณใช้. Net4
Sergej Andrejev

1
@andrefadila - วิธีใช้: string sampleVendorId = ""; int? VendId = sampleVendorId.ToNullable <int> ();
minerva

1
การแปลง ConvConvert จากจากนั้นไม่แปลง T ประเภท nullable ซึ่งทำให้ฟังก์ชั่นนี้ง่ายเคาน์เตอร์น้อย คุณไม่จำเป็นต้องลองใช้ฟังก์ชั่นนี้ โค้ดสามบรรทัดเหล่านี้ทำให้ได้ทั้งหมด: if (string.IsNullOrWhiteSpace (stringObject)) return 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 ไม่ได้แปลงเป็นประเภท nullable ใช้ typeconverter แทน
AaronHS

นั่นคือสิ่งที่ฉันต้องรู้ ... ฉันต้องใช้ชนิดพื้นฐานของ Nullable-Type เมื่อใช้ Convert.ChangeType-Method เพราะมันใช้ไม่ได้กับ Nullable-Typ สำหรับพารามิเตอร์ conversionType
Marcus.D

27

เกี่ยวกับสิ่งนี้:


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

แน่นอนว่านี่ไม่ได้คำนึงถึงการแปลงที่ล้มเหลว


หากคุณส่งคืนค่าใดค่าหนึ่งเป็นสองเท่า (หรือ int ?, ฯลฯ ) จากนั้นจะสามารถแปลงเป็นคู่สุดท้ายได้หรือไม่ ดูการเปลี่ยนแปลงด้านบน
bdukes

ขอโทษด้วยกับเรื่องนั้น. ลืมนักแสดงเสมอจนกว่าคอมไพเลอร์จะกรีดร้อง :)
John Kraft

สิ่งนี้จะล้มเหลวหากคุณไม่เป็นโมฆะและลองใช้จำนวนเงินมีมูลค่าและประกาศจำนวนเป็น var
Steve

23

ฉันเขียนตัวแปลงประเภททั่วไปนี้ มันทำงานได้กับค่า Nullable และค่ามาตรฐานการแปลงระหว่างชนิดที่เปลี่ยนแปลงได้ทั้งหมดไม่ใช่เฉพาะสตริง มันจัดการสถานการณ์ทุกประเภทที่คุณคาดหวัง (ค่าเริ่มต้นค่า null ค่าอื่น ๆ ฯลฯ )

ฉันใช้สิ่งนี้มาเป็นเวลาประมาณหนึ่งปีในโครงการผลิตหลายสิบครั้งดังนั้นมันจึงค่อนข้างแข็งแกร่ง

    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

ฉันชอบคำตอบของโจเอล แต่ฉันแก้ไขมันเล็กน้อยเพราะฉันไม่ใช่แฟนของการกินข้อยกเว้น

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

ฉันสามารถตรวจสอบค่าว่างในหน้าตรรกะ:

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

สวัสดีขอบคุณนี้แก้ไขได้สำหรับฉัน FYI ฉันใช้ 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

1
ก่อนอื่นมีบางอย่างผิดปกติกับคำตอบของคุณและนั่นคือ "คุณสามารถลืมคำตอบอื่น ๆ ทั้งหมด" ซึ่งจะผิดแม้ว่ามันจะเป็นจริง (ซึ่งไม่ใช่) และเกิดอะไรขึ้นกับ "universal solution" นั่นคือมันเต็มไปด้วยประสิทธิภาพที่ไม่ดี ( typeName.IndexOf? จริงๆ?) และพฤติกรรมแปลก ๆ ( TryConvertฟังก์ชั่นที่แสดงไม่ได้จัดการค่า null อย่างถูกต้อง)
Paul Groke

3

นี่คือสิ่งที่ขึ้นอยู่กับคำตอบที่ได้รับการยอมรับ ฉันลบการลอง / จับเพื่อให้แน่ใจว่าข้อยกเว้นทั้งหมดจะไม่ถูกกลืนและไม่ได้รับการจัดการ ตรวจสอบให้แน่ใจด้วยว่าตัวแปรส่งคืน (ในคำตอบที่ยอมรับได้) จะไม่ถูกเตรียมใช้งานสองครั้งโดยไม่ทำอะไรเลย

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

ตัวอย่างประเภท anonimous ของฉัน:

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

อีกรูปแบบหนึ่ง อันนี้

  • ไม่กลืนข้อยกเว้น
  • โยน a NotSupportedExceptionหากไม่สามารถแปลงประเภทได้stringได้ ตัวอย่างเช่น struct แบบกำหนดเองที่ไม่มีตัวแปลงชนิด
  • มิฉะนั้นส่งคืน a (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;
}

1

ลองเพิ่มโซลูชันที่คล้ายกันอีกหนึ่งรายการลงในสแต็ก อันนี้แยกวิเคราะห์ enums และมันก็ดูดี ปลอดภัยมาก.

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