一个通用函数接受引用类型和可空类型以容纳“as”关键字?

时间:2013-02-10 08:52:57

标签: c# generics nullable overload-resolution as-keyword

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

从这个问题Most efficient way to check for DBNull and then assign to a variable?我发现this answer看起来像是:

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)我需要一个选项来指定DBNull s的默认值。如果我没有指定默认值,我还需要一个返回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关键字必须使用check和cast类型。正如我之前所说的,我并不是真的想要从IDataRecord读取或提高性能等方面的解决方案。


有类似的问题

  1. C# generic class using reference types and nullable value types

  2. Is creating a C# generic method that accepts (nullable) value type and reference type possible?

  3. Can a Generic Method handle both Reference and Nullable Value types?

  4. 但这对as关键字非常具体。所以答案不适用。

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

3 个答案:

答案 0 :(得分: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;
}

这样,当您希望在无法转换值时结果为null时,可以使用dr.ReadNullable(field);如果要指定默认值,则可以使用dr.ReadNullable(field, someValue)

如下所述,在原始代码中使用default(T)default(T?)不是必需的,因为无论如何它们总是为空。

答案 1 :(得分: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);

如您所见,需要为引用类型指定默认值。不那么优雅..

答案 2 :(得分: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的行为方式相同。