文字列をnull許容型(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);
}

これで問題なく動作しますが、コードが1行ではなく5行になりました。これにより、特に変換する列が大量にある場合は、読み取りが少し難しくなります。

文字列クラスとジェネリックの拡張を使用して型を渡すと思いましたが、これは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

覚えておくべきもう1つのことは、文字列自体が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タイプ」パラメーターは使用されないため、省略できます。
マイケルメドウズ

1
+1、ちょうど私を倒します。小さなヒント:変換された値は、result.Valueではなく、resultに直接割り当てる必要があります。つまり、「result =(T)conv.ConvertFrom(s);」です。
LukeH 2009

20
これは、.Net4を使用する場合、string.IsNullOrWhiteSpace()で少し簡略化できます
Sergej Andrejev

1
@andrefadila-使用するには:string sampleVendorId = ""; int?vendorId = sampleVendorId.ToNullable <int>();
ミネルバ

1
conv.ConvertFromの呼び出しでは、null許容型のTは変換されないため、この関数は少し直観的に理解できます。この関数ではtry catchは必要ありません。これらの3行のコードですべてが実現します。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
私見これは問題に対する最もエレガントな解決策です
Zaffiro

4
実際..このソリューションは機能しません。changetypeはnull許容型に変換されません。代わりにtypeconverterを使用してください
AaronHS

それが私が知る必要があることです... Convert.ChangeType-Methodを使用するときは、Nullable-Typeの基になる型を使用する必要があります。パラメータconversionTypeのNullable-Typでは機能しないためです。
Marcus.D

27

これはどうですか:


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

もちろん、これは変換の失敗を考慮に入れていません。


いずれかの戻り値をdoubleにキャストした場合、(またはint?など)、それらを最後のdouble?に変換することができます。上記の変更を参照してください。
bdukes

申し訳ありません。コンパイラが悲鳴を上げるまで、キャストは常に忘れてください。:)
ジョン・クラフト

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、2013年

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

ページロジックでnullを確認できます。

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

こんにちはこれで解決しました。ちなみに私はVB.NETを使用しており、VBの同等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>();

誰が反対票を投じていたのか、この普遍的なソリューションの何が問題になっているのかコメントを追加してください。
Pavel Hodek 2012年

1
さて、最初にあなたの答えに非常に悪いことがあり、それが「他のすべての答えを忘れることができる」です。それが真実であったとしても、それは間違っているでしょう(そうではありません)。そして、「普遍的な解決策」の何が悪いのは、それが悪いパフォーマンス(typeName.IndexOf?本当に?)と途方もない振る舞いでいっぱいであることです(示されているTryConvert関数はnull値さえ正しく処理しません)。
Paul Groke、2013年

3

これは受け入れられた答えに基づいたものです。すべての例外が飲み込まれず、処理されないようにするため、try / catchを削除しました。また、(受け入れられた回答の)戻り変数が2度初期化されることがないようにしてください。

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をスローします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

スタックにもう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はそうかもしれませんが、これは1回だけ書かれた、常に使用している単純な12行のコードです。そして、私が言ったように、これらのTypeDescriptor.GetConverter...コードを使用するよりも高速である必要があります。これは別の方法です。
S.Serpooshan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.