Generic方法可以处理Reference和Nullable Value类型吗?

我有一系列的Extension方法来帮助对IDataRecord对象进行空值检查,我目前正在实现这样:

public static int? GetNullableInt32(this IDataRecord dr, int ordinal) { int? nullInt = null; return dr.IsDBNull(ordinal) ? nullInt : dr.GetInt32(ordinal); } public static int? GetNullableInt32(this IDataRecord dr, string fieldname) { int ordinal = dr.GetOrdinal(fieldname); return dr.GetNullableInt32(ordinal); } 

等等,对于我需要处理的每种类型。

我想重新实现这些作为通用方法,部分是为了减少冗余,部分是为了学习如何编写通用方法。

我写过:

 public static Nullable GetNullable(this IDataRecord dr, int ordinal) { Nullable nullValue = null; return dr.IsDBNull(ordinal) ? nullValue : (Nullable) dr.GetValue(ordinal); } 

只要T是值类型,它就可以工作,但如果T是引用类型,它就不会。

如果T是值类型,则此方法需要返回Nullable类型,否则返回default(T)。 我该如何实现这种行为?

您可以像这样声明您的方法:

 public static T GetNullable(this IDataRecord dr, int ordinal) { return dr.IsDBNull(ordinal) ? default(T) : (T) dr.GetValue(ordinal); } 

这样,如果T是可空的int或任何其他可以为null的值类型,它实际上将返回null。 如果它是常规数据类型,它将只返回该类型的默认值。

这有效:

 public static T Get( this IDataRecord dr, int ordinal) { T nullValue = default(T); return dr.IsDBNull(ordinal) ? nullValue : (T) dr.GetValue(ordinal); } public void Code(params string[] args) { IDataRecord dr= null; int? a = Get(dr, 1); string b = Get(dr, 2); } 

我不认为你可以用一个函数来实现它。 如果C#支持基于返回类型的重载,您可能会这样做,但即便如此,我也建议不要这样做。

您应该能够通过不使用可空数据类型来完成相同的事情,并返回实际值或null,如BFree所建议的那样。

我无法理解为什么需要使整个过程复杂化。 为什么不保持它简单明了,并使用以下代码行:

对于null有效的值类型,使用int? iNullable = dr[ordinal] as int?; int? iNullable = dr[ordinal] as int?;

对于null无效的值类型,使用int iNonNullable = dr[ordinal] as int? ?? default(int); int iNonNullable = dr[ordinal] as int? ?? default(int);

对于引用类型,使用string sValue = dr[ordinal] as string;

对于那些认为代码不能工作且dr[ordinal]将为DBNull抛出exception的人来说,这是一个示例方法,一旦给出有效的连接字符串将certificate这个概念。

 private void Test() { int? iTestA; int? iTestB; int iTestC; string sTestA; string sTestB; //Create connection using (SqlConnection oConnection = new SqlConnection(@"")) { //Open connection oConnection.Open(); //Create command using (SqlCommand oCommand = oConnection.CreateCommand()) { //Set command text oCommand.CommandText = "SELECT null, 1, null, null, '1'"; //Create reader using (SqlDataReader oReader = oCommand.ExecuteReader()) { //Read the data oReader.Read(); //Set the values iTestA = oReader[0] as int?; iTestB = oReader[1] as int?; iTestC = oReader[2] as int? ?? -1; sTestA = oReader[3] as string; sTestB = oReader[4] as string; } } } } 

你不能用一种方法来做,但你用三种方法做到这一点:

 public static T GetData(this IDataReader reader, Func getFunc, int index) { if (!reader.IsClosed) { return getFunc(index); } throw new ArgumentException("Reader is closed.", "reader"); } public static T GetDataNullableRef(this IDataReader reader, Func getFunc, int index) where T : class { if (!reader.IsClosed) { return reader.IsDBNull(index) ? null : getFunc(index); } throw new ArgumentException("Reader is closed.", "reader"); } public static T? GetDataNullableValue(this IDataReader reader, Func getFunc, int index) where T : struct { if (!reader.IsClosed) { return reader.IsDBNull(index) ? (T?)null : getFunc(index); } throw new ArgumentException("Reader is closed.", "reader"); } 

然后使用它你会做:

 private static Whatever CreateObject(IDataReader reader) { Int32? id = reader.GetDataNullableValue(reader.GetInt32, 0); string name = reader.GetDataNullableRef(reader.GetString, 1); Int32 x = reader.GetData(reader.GetInt32, 2); } 
 public static T Get(this IDataRecord rec, Func GetValue, int ordinal) { return rec.IsDBNull(ordinal) ? default(T) : GetValue(ordinal); } 

或更高效

 public static T Get(this IDataRecord rec, Func GetValue, int ordinal) { return rec.IsDBNull(ordinal) ? default(T) : GetValue(rec, ordinal); } public static Func GetInt32 = (rec, i) => rec.GetInt32(i); public static Func GetBool = (rec, i) => rec.GetBoolean(i); public static Func GetString = (rec, i) => rec.GetString(i); 

并像这样使用它

 rec.Get(GetString, index); rec.Get(GetInt32, index); 

Nullable结构仅适用于Value类型,因为引用类型无论如何都可以为空…

我是这样做的:

 DataRow record = GetSomeRecord(); int? someNumber = record[15] as int? Guid? someUID = record["MyPrimaryKey"] as Guid?; string someText = GetSomeText(); record["Description"] = someText.ToDbString(); // ........ public static class StringExtensionHelper { public static object ToDbString( this string text ) { object ret = null != text ? text : DBNull.Value return ret; } } 

编辑:您可以(或者您应该)为其他原始类型的课程提供“ToDbInt32,ToDbBool等…”扩展方法。

编辑2:您还可以使用“ToDbValue”扩展基类“object”。

 public static class StringExtensionHelper { public static object ToDbValue( this object value ) { object ret = object.ReferenceEquals( value, null ) ? (object)DBNull.Value : value; return ret; } }