1

これは純粋な好奇心/挑戦であり、実用的な重要性はまったくありません。だから私は仕事を成し遂げる代替ソリューションを探していません.

この質問からDBNullをチェックしてから変数に割り当てる最も効率的な方法は? 次のようなこの回答が見つかりました。

oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault;
oSomeObject.StringMember = oRow["Name"] as string ?? sDefault;

上記の式を 1 つのジェネリック関数 (または 2 つ以上) に移動して、int?andstringの両方を受け入れ、次のように呼び出すことができますか?

oSomeObject.IntMemeber = oRow.Read<int?>("Value", 0); //iDefault is now 0
//or
oSomeObject.IntMemeber = oRow.Read<int>("Value"); //iDefault is now default(int)

//and 
oSomeObject.StringMember = oRow.Read<string>("Name"); //sDefault is now default(string)

要件:

1) DBNulls の場合、デフォルト値を指定するオプションが必要です。default(T)デフォルト値を指定しない場合に備えて、返すオプションも必要です。したがって、これは機能しません:

public static T Read<T>(this IDataRecord dr, string field, T defaultValue) where T : class
{
    return dr[field] as T ?? defaultValue;
}

public static T? Read<T>(this IDataRecord dr, string field, T? defaultValue) where T : struct
{
    return dr[field] as T? ?? defaultValue;
}

電話できないからoSomeObject.StringMemeber = oRow.Read<string>("Name")

オプションのパラメーターである必要はありません。オーバーロードすることもできます。

public static T Read<T>(this IDataRecord dr, string field) where T : class
{
    return dr[field] as T ?? default(T);
}

public static T? Read<T>(this IDataRecord dr, string field) where T : struct
{
    return dr[field] as T? ?? default(T?);
}

public static T Read<T>(this IDataRecord dr, string field, T defaultValue) where T : class
{
    return dr[field] as T ?? defaultValue;
}

public static T? Read<T>(this IDataRecord dr, string field, T? defaultValue) where T : struct
{
    return dr[field] as T? ?? defaultValue;
}

メソッド 1 と 2 はまったく同じ署名を持っているため、これはコンパイルされません。

2) ジェネリック関数 (オーバーロードの場合) は同じ名前にする必要があります。

3)asキーワードは、型のチェックとキャストを使用する必要があります。前に述べたようにIDataRecord、パフォーマンスなどを読み取ったり、強化したりするためのソリューションを実際に探しているわけではありません。


似たような質問があります

  1. 参照型と null 許容値型を使用する C# ジェネリック クラス

  2. (null 許容) 値型と参照型を受け入れる C# ジェネリック メソッドを作成できますか?

  3. ジェネリック メソッドは、参照型と Null 許容値型の両方を処理できますか?

asしかし、これはキーワードに非常に固有です。したがって、そこにある答えは当てはまりません。

補遺: これに対する解決策は 1 つではありません。どちらが最もエレガントな選択肢であるかを受け入れます。

4

3 に答える 3

0

リンク先の回答は、ジェネリック メソッドで参照型と値型の両方を適切な方法で処理する良い方法がないことを明確に示しているようです。そこに示されているように、答えはそれらに異なる名前を付けることです。

defaultValueただし、パラメーターのデフォルト値を使用することで、コードを 4 つではなく 2 つのメソッドに減らすことができます。

public static T Read<T>(this IDataRecord dr, string field, 
                        T defaultValue = null) where T : class
{
    return dr[field] as T ?? defaultValue;
}

public static T? ReadNullable<T>(this IDataRecord dr, string field, 
                                 T? defaultValue = null) where T : struct
{
    return dr[field] as T? ?? defaultValue;
}

この方法はdr.ReadNullable(field)、値をキャストできないときに結果を null にしたい場合やdr.ReadNullable(field, someValue)、デフォルト値を指定したい場合に使用できます。

以下で説明するように、元のコードでdefault(T)anddefault(T?)を使用する必要はありません。これらは常に null になるからです。

于 2013-02-10T09:02:44.863 に答える
0

アイデアは、isの代わりに使用することですas。以下のメソッドはトリックを行います:

    public static T Read<T>(this IDictionary<string, object> dr, string field, T defaultValue)
    {
        var v = dr[field];
        return v is T ? (T)v : defaultValue;
    }

    public static T Read<T>(this IDictionary<string, object> dr, string field)
    {
        var v = dr[field];
        return v is T ? (T)v : default(T);
    }

使用法:

    Dictionary<string, object> d = new Dictionary<string, object>();

    d["s"] = "string";
    d["i"] = 5;
    d["db.null"] = DBNull.Value;

    Console.WriteLine(d.Read("i", 7));                        // 5
    Console.WriteLine(d.Read("s", "default string"));         // string
    Console.WriteLine(d.Read("db.null", "default string"));   // default string
    Console.WriteLine(d.Read("db.null", -1));                 // -1
    Console.WriteLine(d.Read<int>("i"));                      // 5
    Console.WriteLine(d.Read<string>("s"));                   // string
    Console.WriteLine(d.Read<int>("db.null"));                // 0
    Console.WriteLine(d.Read<string>("db.null") ?? "null");   // null

Dictionary簡単な例で使用しましIDataRecordたが、同じように動作します。

于 2013-02-10T09:59:07.597 に答える
0

方法は一つではないようです。JLRisheの答えは良いです。オーバーロードの名前変更に依存しています。これは、関数の名前を変更しないものですが、呼び出しの容易さを妥協するものです (つまり、オプションのパラメーターが 1 つ少ない)。

static T Read<T>(this object obj, T defaultValue) where T : class
{
    return obj as T ?? defaultValue;
}

static T? Read<T>(this object obj, T? defaultValue) where T : struct
{
    return obj as T? ?? defaultValue;
}

public static T Read<T>(this IDataRecord dr, string field, 
                        T defaultValue) where T : class //no optional parameter here :(
{
    return dr[index].Read<T>(defaultValue);
}

public static T? Read<T>(this IDataRecord dr, string field, T? defaultValue = null) 
                        where T : struct
{
    return dr[index].Read<T>(defaultValue);
}

そして、たとえば次のように呼び出します。

Session s = new Session();
s.Id = r.Read("", (byte[])null);
s.UserId = (int)r.Read<int>("");
s.LoginTime = (DateTime)r.Read<DateTime>("");
s.LogoutTime = r.Read("", default(DateTime?));
s.MachineFingerprint = r.Read("", (string)null);

ご覧のとおり、参照型にはデフォルト値を指定する必要があります。エレガントではない..

于 2013-02-10T09:53:29.413 に答える