Chuyển đổi chuỗi thành loại nullable (int, double, v.v ...)


137

Tôi đang cố gắng thực hiện một số chuyển đổi dữ liệu. Thật không may, phần lớn dữ liệu nằm trong chuỗi, trong đó dữ liệu nên là int hoặc double, v.v ...

Vì vậy, những gì tôi đã nhận được là một cái gì đó như:

double? amount = Convert.ToDouble(strAmount);

Vấn đề với cách tiếp cận này là nếu strAmount trống, nếu nó trống tôi muốn nó là null, vì vậy khi tôi thêm nó vào cơ sở dữ liệu, cột sẽ là null. Vì vậy, tôi đã kết thúc việc viết này:

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

Bây giờ điều này hoạt động tốt, nhưng bây giờ tôi có năm dòng mã thay vì một dòng. Điều này làm cho mọi thứ khó đọc hơn một chút, đặc biệt là khi tôi có một số lượng lớn các cột để chuyển đổi.

Tôi nghĩ rằng tôi sử dụng một phần mở rộng cho lớp chuỗi và chung chung để chuyển qua loại, điều này là bởi vì nó có thể là một đôi, hoặc một int hoặc dài. Vì vậy, tôi đã thử điều này:

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

Nhưng tôi gặp lỗi: Không thể chuyển đổi loại 'chuỗi' thành 'T?'

Có cách nào để giái quyết vấn đề này không? Tôi không quen thuộc lắm với việc tạo ra các phương thức sử dụng thuốc generic.


1
Bản sao có thể có của Generic
TryPude

Câu trả lời:


157

Một điều khác cần ghi nhớ là bản thân chuỗi có thể là 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
Bạn có thể bỏ qua tham số "T type" vì nó không được sử dụng.
Michael Meadows

1
+1, chỉ cần đánh bại tôi với nó. Một nitpick nhỏ: giá trị được chuyển đổi cần được gán trực tiếp cho kết quả, không cho kết quả. Giá trị. tức là "result = (T) conv.ConvertFrom (s);".
LukeH

20
Điều này có thể được đơn giản hóa một chút với chuỗi.IsNullOrWhiteSpace () nếu bạn sử dụng .Net4
Sergej Andrejev

1
@andrefadila - Để sử dụng: chuỗi sampleVendorId = ""; int? ellerId = sampleVendorId.ToNullable <int> ();
minerva

1
Cuộc gọi conv.ConvertFrom không chuyển đổi loại T không thể thực hiện được, điều này làm cho chức năng này trở nên trực quan hơn một chút. Bạn không cần thử bắt trong chức năng này. Ba dòng mã này làm cho tất cả: if (string.IsNullOrWhiteSpace (stringObject)) trả về null; var conv = TypeDescriptor.GetConverter (typeof (T)); return (T?) conv.ConvertFrom (stringObject);
David

54

Bạn có thể thử sử dụng phương pháp mở rộng dưới đây:

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

Bằng cách này bạn có thể làm điều này:

double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();

3
IMHO đây là giải pháp thanh lịch nhất cho vấn đề
Zaffiro

4
thực sự .. giải pháp này không hoạt động. changetype không chuyển đổi thành các loại nullable. thay vì sử dụng typeconverter
AaronHS

Đó là những gì tôi cần biết ... Tôi phải sử dụng loại cơ bản của Loại Nullable-Type khi sử dụng Convert.ChangeType-Phương thức. Bởi vì nó không hoạt động với Nullable-Type cho chuyển đổi tham số.
Marcus.D 17/07/19

27

Cái này thì sao:


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

Tất nhiên, điều này không tính đến việc chuyển đổi thất bại.


Nếu bạn bỏ một trong hai giá trị trả về gấp đôi? (hoặc int?, v.v.), sau đó nó sẽ có thể chuyển đổi chúng thành cú đúp cuối cùng?. Xem sự thay đổi ở trên.
bdukes

Xin lỗi vì điều đó. Luôn quên các diễn viên cho đến khi trình biên dịch hét lên. :)
John Kraft

điều này sẽ thất bại nếu bạn không null và bạn thử số tiền.HasValue và khai báo số tiền là var.
Steve

23

Tôi đã viết công cụ chuyển đổi loại chung này. Nó hoạt động với các giá trị Nullable và tiêu chuẩn, chuyển đổi giữa tất cả các loại chuyển đổi - không chỉ chuỗi. Nó xử lý tất cả các loại kịch bản mà bạn mong đợi (giá trị mặc định, giá trị null, giá trị khác, v.v ...)

Tôi đã sử dụng điều này trong khoảng một năm trong hàng chục chương trình sản xuất, vì vậy nó khá vững chắc.

    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
Tôi không nghĩ bỏ qua tất cả các lỗi chuyển đổi là điều nên làm. Ngoài ra, bạn có lẽ không nên nuốt tất cả các loại ngoại lệ. Ít nhất là ném lại OutOfMemoryExceptionnếu bạn không thể thu hẹp nó thành một tập hợp các loại ngoại lệ cố định.
Paul Groke

9

Bạn có thể muốn thử:

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

tự kiểm tra null và trả lại int?nếu cần thiết. Bạn cũng sẽ muốn bọc nó trong mộttry {}


6

Hãy bắn nó ...

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

Sau đó gọi nó như thế này ...

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

Tôi thích câu trả lời của Joel, nhưng tôi đã sửa đổi nó một chút vì tôi không phải là người thích ăn ngoại lệ.

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

Bạn có thể sử dụng các mục sau với các đối tượng, thật không may, điều này không hoạt động với các chuỗi.

double? amount = (double?)someObject;

Tôi sử dụng nó để gói một biến phiên trong một thuộc tính (trên trang cơ sở) .. vì vậy cách sử dụng thực tế của tôi là (trong trang cơ sở của tôi):

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

Tôi có thể kiểm tra null trong logic trang:

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

Hi cảm ơn điều này đã giải quyết nó cho tôi. FYI Tôi đã sử dụng VB.NET và tương đương VB CType(Object, Nullable(Of Double))hoạt động tốt với các chuỗi
rayzinnz

Có phiên bản ví dụ đầu tiên của bạn có thể được sử dụng với chuỗi không?
wazz

3

Không có cách nào xung quanh điều này. Nullable, cũng như phương thức của bạn, bị hạn chế chỉ sử dụng các loại giá trị làm đối số. Chuỗi là một loại tham chiếu và do đó không tương thích với khai báo này.


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

Có một giải pháp chung (cho bất kỳ loại nào). Khả năng sử dụng là tốt, nhưng việc triển khai nên được cải thiện: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Điều này cho phép bạn viết mã rất sạch như thế này:

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

và cũng:

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

Ai đã hạ cấp xin vui lòng thêm một nhận xét có gì sai với giải pháp phổ quát này.
Pavel Hodek

1
Chà, đầu tiên có điều gì đó rất sai với câu trả lời của bạn và đó là "bạn có thể quên tất cả các câu trả lời khác". Điều đó sẽ sai ngay cả khi nó là đúng (điều đó không đúng). Và điều không đúng với "giải pháp phổ quát" là nó chứa đầy hiệu năng kém ( typeName.IndexOfthực sự?) Và hành vi lạ ( TryConvertchức năng được hiển thị thậm chí không xử lý chính xác các giá trị null).
Paul Groke

3

Đây là một cái gì đó dựa trên câu trả lời được chấp nhận. Tôi đã loại bỏ thử / bắt để đảm bảo tất cả các ngoại lệ không bị nuốt và không được xử lý. Cũng đảm bảo rằng biến trả về (trong câu trả lời được chấp nhận) không bao giờ được khởi tạo hai lần cho không có gì.

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

Ví dụ của tôi cho các loại không đồng nhất:

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

Một biến thể khác. Cái này

  • Không nuốt ngoại lệ
  • Ném a NotSupportedExceptionnếu loại không thể được chuyển đổi từ string. Ví dụ, một cấu trúc tùy chỉnh mà không có bộ chuyển đổi loại.
  • Mặt khác trả về a (T?)nullnếu chuỗi không phân tích cú pháp. Không cần phải kiểm tra null hoặc khoảng trắng.
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

Hãy thêm một giải pháp tương tự vào ngăn xếp. Điều này cũng phân tích enums, và nó trông tốt đẹp. Rất an toàn.

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

Câu trả lời chung được cung cấp bởi " Joel Coehoorn " là tốt.

Nhưng, đây là một cách khác mà không cần sử dụng những khối GetConverter...hoặc try/catchkhối đó ... (tôi không chắc nhưng điều này có thể có hiệu suất tốt hơn trong một số trường hợp):

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

Cách sử dụng như sau:

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 có thể, nhưng nó là một mã 12 dòng đơn giản, được viết một lần, nhưng sử dụng tất cả các lần. Và, như tôi đã nói, nó nên / có thể nhanh hơn sử dụng những TypeDescriptor.GetConverter... mã đó. Đây chỉ là một cách khác.
S.Serpooshan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.