C# 一个泛型函数,可接受引用类型和可空类型,以适应;作为「;关键词可能吗?
这纯粹是好奇/挑战,根本没有实际意义因此,我不会寻找其他解决方案来完成工作。 从这个问题中,我发现了如下问题:C# 一个泛型函数,可接受引用类型和可空类型,以适应;作为「;关键词可能吗?,c#,generics,nullable,overload-resolution,as-keyword,C#,Generics,Nullable,Overload Resolution,As Keyword,这纯粹是好奇/挑战,根本没有实际意义因此,我不会寻找其他解决方案来完成工作。 从这个问题中,我发现了如下问题: oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault; oSomeObject.StringMember = oRow["Name"] as string ?? sDefault; 我是否可以将上述表达式移动到一个通用函数(或两个或多个),使其同时接受int?和string,并且可以像这样调用它: oSomeObject
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)
因为我不能调用oSomeObject.StringMemeber=oRow.Read(“名称”)
它不必是可选参数,甚至可以是重载:
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;
}
publicstatict-Read(此IDataRecord dr,字符串字段),其中T:class
{
将dr[字段]返回为T??默认值(T);
}
公共静态T?读取(此IDataRecord dr,字符串字段),其中T:struct
{
将dr[字段]返回为T???默认值(T?);
}
公共静态T读(此IDataRecord dr,字符串字段,T defaultValue),其中T:class
{
返回dr[字段]作为T??默认值;
}
公共静态T?读取(此IDataRecord dr,字符串字段,T?默认值),其中T:struct
{
将dr[字段]返回为T???默认值;
}
这不会编译,因为方法1和方法2具有相同的签名
2) 泛型函数(在重载情况下)应具有相同的名称
3) as
关键字必须用于检查和转换类型。如前所述,我并不是真的在寻找解决方案来阅读IDataRecord
,或者提高性能或其他东西
也有类似的问题 但这对于
作为
关键字是非常特殊的。因此,那里的答案并不适用
附录:我知道不会有一个单一的解决方案。我接受最优雅的选择。您链接的答案似乎非常清楚地表明,没有一种好方法可以让泛型方法以您希望的干净方式处理引用类型和值类型。如图所示,答案似乎是给他们起不同的名字 但是,通过使用
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;
}
public static T Read(此IDataRecord dr,字符串字段,
T defaultValue=null),其中T:class
{
返回dr[字段]作为T??默认值;
}
公共静态T?ReadNullable(此IDataRecord dr,字符串字段,
T?defaultValue=null),其中T:struct
{
将dr[field]返回为T???Default值;
}
这样,当无法强制转换值时希望结果为空时,可以使用dr.ReadNullable(field)
;当希望指定默认值时,可以使用dr.ReadNullable(field,someValue)
正如下面所讨论的,在原始代码中使用
default(T)
和default(T?
是没有必要的,因为它们总是空的。似乎没有一种方法。杰里什的回答很好。它依赖于重命名重载。这是一个不重命名函数,但在调用方便性上有所妥协的方法(即少一个可选参数)
static T Read(此对象obj,T defaultValue),其中T:class
{
返回obj作为T??默认值;
}
静态T?读取(此对象对象,T?默认值),其中T:struct
{
返回obj作为T???默认值;
}
公共静态T读(此IDataRecord dr,字符串字段,
T defaultValue),其中T:class//此处没有可选参数:(
{
返回dr[index]。读取(默认值);
}
公共静态T?读取(此IDataRecord dr,字符串字段,T?defaultValue=null)
其中T:struct
{
返回dr[index].Read(默认值);
}
打电话,例如:
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);
sessions=newsession();
s、 Id=r.Read(“,(字节[])null);
s、 UserId=(int)r.Read(“”);
s、 LoginTime=(DateTime)r.Read(“”);
s、 LogoutTime=r.Read(“),默认值(DateTime?);
s、 MachineFingerprint=r.Read(“,(字符串)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);
}
public static T Read(此IDictionary dr,字符串字段,T defaultValue)
{
var v=dr[字段];
返回v为T?(T)v:默认值;
}
公共静态T读(此IDictionary dr,字符串字段)
{
var v=dr[字段];
返回v为T?(T)v:默认值(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 d=newdictionary();
d[“s”]=“字符串”;
d[“i”]=5;
d[“db.null”]=DBNull.Value;
Console.WriteLine(d.Read(“i”,7));//5
Console.WriteLine(d.Read(“s”,“默认字符串”);//字符串
Console.WriteLine(d.Read(“db.null”,“默认字符串”);//默认字符串
Console.WriteLine(d.Read(“db.null”,-1));//-1
Console.WriteLine(d.Read(“i”);//5
Console.WriteLine(d.Read(“s”);//字符串
Console.WriteLine(d.Read(“db.null”);//0
Console.WriteLine(d.Read(“db.null”)??“null”);//null
我使用了
Dictionary
作为快速示例,IDataRecord
将以相同的方式运行。“Generic”意味着相同的IL可以用于与约束匹配的所有类型。问题是引用类型和可空值类型要求同一运算符使用不同的IL(as
)。因此,您需要不同的方法。而同名的方法在其参数列表中必须有所不同。因此,除非您正在寻找一种解决方案,例如,在运行时为每种类型编译一些IL,否则我会说它不会比您已有的更优雅。嗯,但遗憾的是,我还没有解决方案。请为您的me使用不同的名称方法?是的,这总是可能的。看来这是最重要的
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