ジェネリックパラメーターとしてnull可能な型は可能ですか?


287

私はこのようなことをしたいです:

myYear = record.GetValueOrNull<int?>("myYear"),

ジェネリックパラメーターとしてnull許容型を確認します。

GetValueOrNull関数がnullを返す可能性があるため、最初の試みは次のとおりです。

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : class
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
    {
        return (T)columnValue;
    }
    return null;
}

しかし、私が今得ているエラーは:

タイプ「int?」ジェネリック型またはメソッドでパラメーター 'T'として使用するには、参照型である必要があります

正しい!Nullable<int>ですstruct!だから私はクラスの制約を制約に変更しようとしましたstruct(そして副作用としてnullこれ以上返すことはできません):

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : struct

今割り当て:

myYear = record.GetValueOrNull<int?>("myYear");

次のエラーが発生します。

タイプ「int?」ジェネリック型またはジェネリックメソッドでパラメーター 'T'として使用するには、null不可の値型である必要があります

null許容型をジェネリックパラメーターとして指定することは可能ですか?


3
Pls pls IDataRecordDbDataRecord.. から署名を作成します
nawfal 2013

回答:


262

戻り値の型をNullableに変更し、Nullableでないパラメーターを使用してメソッドを呼び出します

static void Main(string[] args)
{
    int? i = GetValueOrNull<int>(null, string.Empty);
}


public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
        return (T)columnValue;

    return null;
}

1
'is'演算子の代わりに "columnValue == DBNull.Value"を使用することをお勧めします。これは少し速いためです=)
driAn

40
個人的な好みですが、短縮形Tを使用できますか?Nullable <T>の代わり
Dunc

11
これは値型には問題ありませんが、C#はNullable <(ref type)>のように「文字列?」のように見えないため、参照型(GetValueOrNull <string>など)ではまったく機能しないと思います。ロバートCバースとジェームスジョーンズのソリューションは、以下のように、それがあなたのニーズである場合、私にははるかに良いように見えます。
bacar

2
@bacar-右、つまり「where T:struct」、参照型が必要な場合は、「where T:class」を使用して同様のメソッドを作成できます
Greg Dean

4
@Greg-確かに、2番目の方法が必要で、名前をオーバーロードすることはできません。私が言うように、val型とref型の両方を処理したい場合は、このページに、より明確な解決策が示されていると思います。
bacar 2011

107
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
    object val = rdr[index];

    if (!(val is DBNull))
        return (T)val;

    return default(T);
}

次のように使用してください:

decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);

6
これは、次のように短縮できます。return rdr.IsDBNull(index)?default(T):(T)rdr [index];
Foole

11
この質問では、default(T)ではなく明示的にnullが必要だと思います。
mafu 2014

5
@mafu default(T)は、参照型の場合はnullを返し、数値型の場合は0を返すため、ソリューションがより柔軟になります。
James Jones

2
これGetValueOrDefaultを呼び出すのではdefault(T)なく、返すことを明確にするためにこれを呼び出す方が明確だと思いますnull。あるいは、がTnull可能でない場合に例外をスローさせることもできます。
2014

この方法には多くの利点があり、null以外の値を返すことも考えなければなりません。
シェーン

61

元のコードに対して2つのことを行うだけです。where制約を削除し、最後のコードをreturnからreturn nullに変更しreturn default(T)ます。このようにして、必要なタイプを返すことができます。

ところで、ステートメントをにis変更することで、の使用を回避できます。ifif (columnValue != DBNull.Value)


4
NULLと0の間の論理的な差があるため、このソリューションは、動作しません
グレッグ・ディーン

15
彼が渡す型がint?であれば機能します。彼が望むように、それはNULLを返します。タイプとしてintを渡すと、intはNULLにできないため、0を返します。私がそれを試してみて完全に機能するという事実に加えて。
ロバートC.バース

2
これが最も正確で柔軟な答えです。ただし、これreturn defaultで十分です(は必要ありません。(T)コンパイラは署名の戻り値の型から推測します)。
McGuireV10

5

免責事項:この回答は機能しますが、教育のみを目的としています。:)ジェームズ・ジョーンズの解決策はおそらくここで最高であり、確かに私が一緒に行くものです。

C#4.0のdynamicキーワードは、安全性が低くても、これをさらに簡単にします。

public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
  var val = reader[columnName];

  return (val == DBNull.Value ? null : val);
}

これで、RHSに明示的な型のヒントを与える必要がなくなりました。

int? value = myDataReader.GetNullableValue("MyColumnName");

実際、あなたはそれをまったく必要としません!

var value = myDataReader.GetNullableValue("MyColumnName");

value これで、int、文字列、またはDBから返されたタイプになります。

唯一の問題は、これがLHSでnull不可の型を使用することを妨げないことです。その場合、次のようなかなり厄介なランタイム例外が発生します。

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type

を使用するすべてのコードと同様に、dynamic警告コーダー。


4

これに似た信じられないようなことをしなければなりませんでした。私のコード:

public T IsNull<T>(this object value, T nullAlterative)
{
    if(value != DBNull.Value)
    {
        Type type = typeof(T);
        if (type.IsGenericType && 
            type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
        {
            type = Nullable.GetUnderlyingType(type);
        }

        return (T)(type.IsEnum ? Enum.ToObject(type, Convert.ToInt32(value)) :
            Convert.ChangeType(value, type));
    }
    else 
        return nullAlternative;
}

3

Reference型とstruct型を扱いたいと思います。XML要素の文字列をより型付けされた型に変換するために使用します。リフレクションを使用してnullAlternativeを削除できます。formatproviderは、カルチャに依存する「。」を処理します。または '、'区切り記号(例:10進数、int、double)。これはうまくいくかもしれません:

public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null ) 
    {
        IFormatProvider theProvider = provider == null ? Provider : provider;
        XElement elm = GetUniqueXElement(strElementNameToSearchFor);

        if (elm == null)
        {
            object o =  Activator.CreateInstance(typeof(T));
            return (T)o; 
        }
        else
        {
            try
            {
                Type type = typeof(T);
                if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
                {
                    type = Nullable.GetUnderlyingType(type);
                }
                return (T)Convert.ChangeType(elm.Value, type, theProvider); 
            }
            catch (Exception)
            {
                object o = Activator.CreateInstance(typeof(T));
                return (T)o; 
            }
        }
    }

次のように使用できます。

iRes = helper.GetValueOrNull<int?>("top_overrun_length");
Assert.AreEqual(100, iRes);



decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees");
Assert.AreEqual(new Decimal(10.1), dRes);

String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees");
Assert.AreEqual("10.1", strRes);

2

これはデッドスレッドかもしれませんが、私は以下を使用する傾向があります:

public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct 
{
    return reader[columnName] as T?;
}

1
"型 'T'は、ジェネリック型またはメソッド 'Nullable <T>'のパラメーター 'T'として使用するために、null不可の値型でなければなりません"
Ian Warburton

1

私自身も同じ問題に遭遇しました。

... = reader["myYear"] as int?; 動作し、きれいです。

問題なくすべてのタイプで動作します。結果がDBNullの場合、変換が失敗したためnullを返します。


実際、のint v=reader["myYear"]??-1;代わりに、おそらく他のデフォルトを実行することができます-1。ただし、値がDBNull...の場合、問題が発生する可能性があります
nurchi

1

私はこれが古いことを知っていますが、ここに別の解決策があります:

public static bool GetValueOrDefault<T>(this SqlDataReader Reader, string ColumnName, out T Result)
{
    try
    {
        object ColumnValue = Reader[ColumnName];

        Result = (ColumnValue!=null && ColumnValue != DBNull.Value) ? (T)ColumnValue : default(T);

        return ColumnValue!=null && ColumnValue != DBNull.Value;
    }
    catch
    {
        // Possibly an invalid cast?
        return false;
    }
}

さて、T値型か参照型かは関係ありません。関数がtrueを返す場合にのみ、データベースから適切な値が得られます。使用法:

...
decimal Quantity;
if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity))
{
    // Do something with Quantity
}

このアプローチは、 int.TryParse("123", out MyInt);


命名規則に取り組んだ方がいいでしょう。彼らは一貫性に欠けています。ある場所には大文字のない変数があり、次にある変数があります。メソッドのパラメーターについても同様です。
MarinoŠimić17年

1
完了しました。コードが今より良く見えることを願っています。ボブはあなたのおばさんです:)すべてはskookumです
nurchi

0

複数の一般的な制約は、ANDの方法(より制限的)でのみ、ORの方法(制限の少ない)で組み合わせることはできません。つまり、1つの方法で両方のシナリオを処理することはできません。一般的な制約を使用してメソッドに一意の署名を作成することもできないため、2つの個別のメソッド名を使用する必要があります。

ただし、一般的な制約を使用して、メソッドが正しく使用されていることを確認できます。

私の場合は、nullを返すことを特に望んでおり、考えられる値の型のデフォルト値は決して返しません。GetValueOrDefault =悪い。GetValueOrNull =良い。

「Null」と「Nullable」という言葉を使用して、参照型と値型を区別しました。そして、これがSystem.Linq.EnumerableクラスのFirstOrDefaultメソッドを補完する私が書いたいくつかの拡張メソッドの例です。

    public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source)
        where TSource: class
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a class is null
        return result;
    }

    public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source)
        where TSource : struct
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a nullable is null
        return result;
    }

0

より短い方法:

public static T ValueOrDefault<T>(this DataRow reader, string columnName) => 
        reader.IsNull(columnName) ? default : (T) reader[columnName];

復帰0のためにint、とnullのためにint?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.