Dizeyi boş karakter türüne dönüştürme (int, double vb.)


137

Bazı veri dönüşümleri yapmaya çalışıyorum. Ne yazık ki, verilerin çoğu int veya double, vb olması gereken dizelerdedir.

Yani ne var gibi bir şey:

double? amount = Convert.ToDouble(strAmount);

Bu yaklaşım ile ilgili sorun strAmount boş ise, eğer boşsa null değerinde olmasını istiyorum, bu yüzden veritabanına eklediğimde sütun null olacaktır. Bu yüzden bunu yazdım:

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

Şimdi bu iyi çalışıyor, ama şimdi bir yerine beş kod satırı var. Bu, özellikle dönüştürülecek çok sayıda sütunum olduğunda, işleri okumayı biraz daha zorlaştırır.

Ben dize sınıfına bir uzantısı kullanmak düşündüm ve türünü geçmek için genel 's, çünkü bu bir çift, int veya uzun olabilir. Bu yüzden denedim:

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

Ama hatayı alıyorum: 'string' tipi 'T'ye' dönüştürülemiyor mu?

Bunun etrafında bir yol var mı? Jenerikler kullanarak yöntemler oluşturmaya pek aşina değilim.


Yanıtlar:


157

Akılda tutulması gereken başka bir şey de ipin kendisinin boş olabileceğidir.

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
Kullanılmadığı için "T tipi" parametresini atlayabilirsiniz.
Michael Meadows

1
+1, Sadece beni dövdü. Küçük bir nitpick: dönüştürülen değerin, sonuca değil, doğrudan sonuca atanması gerekir. yani, "sonuç = (T) dönş. ConvertFrom (lar);".
LukeH

20
.Net4 kullanıyorsanız string.IsNullOrWhiteSpace () ile biraz basitleştirilebilir. Net4
Sergej Andrejev

1
@andrefadila - Kullanmak için: string sampleVendorId = ""; int? vendorId = sampleVendorId.ToNullable <int> ();
minerva

1
Conv.ConvertFrom çağrısı, boş bir T türünü dönüştürmez, bu da bu işlevi biraz sezgisel hale getirir. Bu işlevde bir deneme yakalamanız gerekmez. Bu üç kod satırı hepsini yapar: if (string.IsNullOrWhiteSpace (stringObject)) return null; var conv = TypeDescriptor.GetConverter (typeof (T)); return (T?) dönş.ConvertFrom (stringObject);
David

54

Aşağıdaki uzantı yöntemini kullanmayı deneyebilirsiniz:

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

Bu şekilde şunları yapabilirsiniz:

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

3
IMHO bu sorunun en zarif çözümü
Zaffiro

4
aslında .. bu çözüm işe yaramıyor. changetype null olabilecek türlere dönüşmez. bunun yerine typeconverter kullanın
AaronHS

Bilmem gereken budur ... Convert.ChangeType-Method kullanırken Nullable-Type'ın temelini kullanmak zorundayım. Parametre conversionType için bir Nullable-Typ ile çalışmadığından.
Marcus.D

27

Peki buna ne dersin:


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

Tabii ki, bu dönüştürme başarısız olduğunu dikkate almaz.


Dönüş değerlerinden herhangi birini iki katına çıkarırsanız? (veya int ?, vb.), bunları son çifte dönüştürebilecek mi ?. Yukarıdaki değişikliğe bakın.
bdukes

Bunun için üzgünüm. Derleyici çığlık atıncaya kadar almayı unutun. :)
John Kraft

null değilseniz ve miktarı denerseniz bu başarısız olur.HasDeğer ve miktarı var olarak beyan edin.
Steve

23

Bu genel tip dönüştürücüyü yazdım. Yalnızca dizge değil, tüm dönüştürülebilir tipler arasında dönüştürme yaparak Nullable ve standart değerlerle çalışır. Beklediğiniz her türlü senaryoyu işler (varsayılan değerler, boş değerler, diğer değerler, vb.)

Bunu yaklaşık bir yıldır düzinelerce üretim programında kullanıyorum, bu yüzden oldukça sağlam olmalı.

    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üm dönüşüm hatalarını görmezden gelmenin doğru bir şey olduğunu düşünmüyorum. Ayrıca muhtemelen her türlü istisnayı yutmamalısınız. En azından OutOfMemoryExceptionsabit bir istisna türü kümesine daraltamazsanız yeniden atın .
Paul Groke

9

Denemek isteyebilirsiniz:

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

kendi null kontrolünüzü yapın ve int?gerekirse iade edin. Ayrıca bunu birtry {}


6

Bunu deneyin ...

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

Öyleyse şöyle deyin ...

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'in cevabını seviyorum ama istisnaları yemeyi sevmediğim için biraz değiştirdim.

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

Aşağıdakileri nesnelerle kullanabilirsiniz, maalesef bu dizelerle çalışmaz.

double? amount = (double?)someObject;

Ben bir oturum değişkeni bir özellik (temel sayfada) sarma için kullanın .. böylece benim gerçek kullanım (temel sayfamda):

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

Sayfa mantığında null olup olmadığını kontrol edebiliyorum:

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

Merhaba, bu benim için çözdü. FYI VB.NET kullanıyordum ve VB CType(Object, Nullable(Of Double))
eşdeğeri

ilk örneğinizin dizelerle kullanılabilecek bir sürümü var mı?
wazz

3

Etrafta yol bulunmuyor. Nullable ve yönteminiz argüman olarak yalnızca değer türlerini kullanmakla sınırlıdır. Dize bir başvuru türüdür ve bu nedenle bu bildirimle uyumlu değildir.


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

Genel bir çözüm vardır (her tür için). Kullanılabilirlik iyidir, ancak uygulama geliştirilmelidir: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Bu, aşağıdaki gibi çok temiz bir kod yazmanıza izin verir:

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

ve ayrıca:

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

Kimler aşağı çekiyordu lütfen bu evrensel çözümde neyin yanlış olduğunu yorumlayın.
Pavel Hodek

1
Öncelikle cevabınızla ilgili çok yanlış bir şey var ve bu da “diğer tüm cevapları unutabilirsiniz”. Bu doğru olsa bile yanlış olurdu (ki bu doğru değildir). Ve "evrensel çözüm" ile ilgili yanlış olan şey, kötü performans ( typeName.IndexOf? Gerçekten?) Ve boğulma davranışı (gösterilen TryConvertişlev null değerleri doğru şekilde işlemez) ile dolu olmasıdır.
Paul Groke

3

İşte kabul edilen cevaba dayanan bir şey. Tüm istisnaların yutulmadığından ve ele alınmadığından emin olmak için denemeyi / yakalamayı kaldırdım. Ayrıca dönüş değişkeninin (kabul edilen cevapta) hiçbir zaman hiçbir zaman iki kez başlatılmadığından emin olun.

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

Anonim tipler için örnek:

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

Başka bir varyasyon. Bu

  • İstisnaları yutmaz
  • NotSupportedExceptionTür dönüştürülemiyorsa a atarstring . Örneğin, tür dönüştürücü olmayan özel bir yapı.
  • Aksi takdirde (T?)null, dize ayrıştırılamazsa a değerini döndürür . Boş veya boşluk olup olmadığını kontrol etmeye gerek yok.
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

Yığına benzer bir çözüm daha ekleyelim. Bu aynı zamanda numaralandırmalar ayrıştırır ve güzel görünüyor. Çok güvenli.

/// <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 " tarafından verilen genel cevap iyidir.

Ancak, bu GetConverter...veya try/catchblokları kullanmadan başka bir yol ... (emin değilim ama bu bazı durumlarda daha iyi performans olabilir):

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

Kullanımı aşağıdaki gibidir:

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 olabilir, ancak bir kez yazılan, ancak her zaman kullanılan basit bir 12 satırlık koddur. Ve dediğim gibi, bu TypeDescriptor.GetConverter... kodları kullanmaktan daha hızlı olmalı / olabilir . Bu sadece başka bir yol.
S.Serpooshan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.