一个既接受引用类型又接受可为null类型的泛型函数;作为“;关键字可能
本文关键字:函数 泛型 作为 关键字 类型 null 引用类型 一个 | 更新日期: 2023-09-27 17:58:43
这纯粹是好奇/挑战,根本没有实际意义所以我不是在寻找能完成任务的替代解决方案
从这个问题来看,检查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) 如果是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
关键字检查和强制转换类型。正如我之前所说的,我并不是真的在寻找从IDataRecord
中读取或提高性能之类的解决方案。
也有类似的问题
使用引用类型和可为null的值类型的C#泛型类
创建一个接受(可为null)值类型和引用类型的C#泛型方法可能吗?
泛型方法可以同时处理引用和可为null的值类型吗?
但这是非常具体的as
关键字。因此,那里的答案并不适用。
附录:我知道不会有一个单一的解决方案。我会接受哪一种是最优雅的选择。
您链接到的答案似乎非常清楚地表明,没有一种好的方法可以让泛型方法以您想要的干净方式处理引用类型和值类型。如图所示,答案似乎是给它们起不同的名字。
但是,通过使用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?)
是不必要的,因为它们无论如何都将为null。
想法是使用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
将以相同的方式进行操作。
似乎没有一种方法。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);
正如您所看到的,引用类型需要指定一个默认值。不那么优雅。。