1

这纯粹是好奇/挑战,根本没有实际意义。所以我不是在寻找完成工作的替代解决方案。

从这个问题最有效的方法来检查 DBNull 然后分配给一个变量?我发现这个答案看起来像:

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

我可以将上述表达式移动到一个通用函数(或两个,或更多),以便它同时接受两者int?string并且我可以这样称呼它:

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) 在 s 的情况下,我需要一个选项来指定默认值DBNulldefault(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. 使用引用类型和可为空值类型的 C# 泛型类

  2. 是否可以创建一个接受(可为空)值类型和引用类型的 C# 泛型方法?

  3. 通用方法可以同时处理引用和可空值类型吗?

但这是非常特定于as关键字的。所以那里的答案不适用。

附录:我知道不会有一个单一的解决方案。我会接受哪个是最优雅的选择。

4

3 回答 3

0

您链接到的答案似乎很清楚地表明,没有一种好方法可以让泛型方法以您想要的干净方式处理引用类型和值类型。如图所示,答案似乎是给它们起不同的名字。

但是,您可以通过使用defaultValue参数的默认值将代码减少到只有两种方法而不是四种方法:

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?)是不必要的,因为无论如何它们都将始终为空。

于 2013-02-10T09:02:44.863 回答
0

似乎没有一种方法。JLRishe 的回答很好。它依赖于重命名重载。这是一些不重命名函数,但在调用方便性上的妥协(即少一个可选参数)

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 回答
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 回答