web-dev-qa-db-ja.com

汎用パラメーターとしてNullable型は可能ですか?

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

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許容型をジェネリックパラメーターとして指定することは可能ですか?

251
Tom Pester

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

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;
}
236
Greg Dean
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);
102
James Jones

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

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

54
Robert C. Barth

免責事項:この回答は機能しますが、教育目的のみを目的としています。 :)ジェームズジョーンズのソリューションはおそらくここで最高であり、間違いなく私が行くだろう。

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:警告コーダーを使用するすべてのコードと同様。

5
Ian Kemp

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

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

参照型と構造体型を処理したいと思います。これを使用して、XML要素の文字列をより型付けされた型に変換します。リフレクションを使用してnullAlternativeを削除できます。 formatproviderは、カルチャ依存の「。」を処理します。または、「、」セパレータ小数または整数と倍精度。これはうまくいくかもしれません:

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);
3
Roland Roos

これはデッドスレッドの可能性がありますが、以下を使用する傾向があります。

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

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

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);と非常に似ています

1
nurchi

私は自分で同じ問題に遭遇しました。

... = reader["myYear"] as int?;は動作し、クリーンです。

どのタイプでも問題なく機能します。結果がDBNullの場合、変換が失敗するとnullを返します。

1
Hele

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

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

私の場合、nullが返されることを特に望んでおり、可能な値の種類のデフォルト値は決してありません。 GetValueOrDefault = bad。 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
Casey