C# 用于从可能为空的db值分配值的优雅语法
对于某些上下文,记录类SaleAmount属性为C# 用于从可能为空的db值分配值的优雅语法,c#,C#,对于某些上下文,记录类SaleAmount属性为 public decimal? SaleAmount { get; set; } 理想情况下,我会这样做 record.SaleAmount.Value = sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? null : sql.Reader.GetDecimal(IDX_SALEAMOUNT); 唉,编译器和这不是朋友,因为 无法确定条件表达式的类型,因为“”和“decimal”之间没有
public decimal? SaleAmount
{
get;
set;
}
理想情况下,我会这样做
record.SaleAmount.Value =
sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? null :
sql.Reader.GetDecimal(IDX_SALEAMOUNT);
唉,编译器和这不是朋友,因为
无法确定条件表达式的类型,因为“”和“decimal”之间没有隐式转换
那么,你会如何优雅地表达这一点,而不是像下面这样玩明显的牌
if (!sql.Reader.IsDBNull(IDX_SALEAMOUNT))
record.SaleAmount = sql.Reader.GetDecimal(IDX_SALEAMOUNT);
还是高于最佳解决方案?您会得到该错误,因为类型
decimal
(值类型)和null
(参考值)不能一起使用
此外,由于您正在为可空
类型的值
属性赋值,因此您实际上是在尝试将空
赋值给十进制
类型,而该类型同样是不可行的
您可以通过强制转换将GetDecimal
结果显式设置为可为null的decimal
类型:
record.SaleAmount = sql.Reader.IsDBNull(IDX_SALEAMOUNT) ?
null : (decimal?)sql.Reader.GetDecimal(IDX_SALEAMOUNT);
不过,我同意你的第二种方法。你可以选择:
record.SaleAmount =
(sql.Reader.IsDBNull(IDX_SALEAMOUNT) ? null :
sql.Reader.GetDecimal(IDX_SALEAMOUNT)) as decimal?;
您可以尝试另一种方法:
// usage: new GenericDataReader(cm.ExecuteReader())
// ...Get<decimal?>(...);
public class GenericDataReader : IDataReader
{
// IDataReader implementation
public T Get<T>(int ordinal)
{
if (_dataReader.IsDBNull(ordinal))
return default(T);
else
return (T)_dataReader.GetValue(ordinal);
}
}
//用法:新的GenericDataReader(cm.ExecuteReader())
//…得到(…);
公共类GenericDataReader:IDataReader
{
//IDataReader实现
公共T获取(整数顺序)
{
if(_dataReader.IsDBNull(序号))
返回默认值(T);
其他的
return(T)u dataReader.GetValue(序数);
}
}
有关此方法的更多信息。为什么不使用空合并运算符
record.SaleAmount = (decimal?)sql.Reader.GetDecimal(IDX_SALEAMOUNT)
?? null;
如果您使用的是C#3,我会使用一种扩展方法让您自己更容易做到这一点:
public static decimal? GetNullableDecimal(this DbReader reader,
int column)
{
return reader.IsDBNull(column) ? (decimal?) null :
reader.GetDecimal(column);
}
然后您的应用程序代码就可以使用:
record.SaleAmount = sql.Reader.GetNullableDecimal(IDX_SALEAMOUNT);
首先,Value属性是只读的,因此无论如何都不能分配给它 如果向条件的false部分添加强制转换以强制转换为可为null的十进制,编译器应该会很高兴。试试下面的方法
record.SaleAmount = sql.Reader.IsDBNull(IDX_SALEAMOUNT) ?
null : (decimal?) sql.Reader.GetDecimal(IDX_SALEAMOUNT);
对我来说,最干净的解决方案是将其从业务逻辑层中完全删除,并首先让查询向读取器返回一个非空值 例如,如果使用SQL Server
SELECT IsNull(SalesAmount,0) ...
我将FieldHelper类与可为空的ToXXX(object)方法一起使用。这是我的建议 十进制示例(这是在Linq之前的日子里写的-大约2-3年前, 欢迎您将委托构造替换为Linq):
//
///获取一个可为空的值。
///
///要转换的值。
///转换后的值。
公共静态Nullable ToDecimal(对象可用)
{
返回音调可调(aValue,
委托(对象A可转换值)
{
返回Convert.ToDecimal(aConvertableValue);
});
}
///
///如有必要,转换给定值。
///
///来自数据库的值。
///转换委托。
///null或给定的值。
私有静态可空可调(
对象变量,转换器(转换器),其中T:struct
{
if(aValue==DBNull.Value | | aValue==null)
返回null;
else if(aValue是T)
返回(T)aValue;
其他的
返回一个转换器(aValue);
}
我喜欢使用以下通用扩展方法(可以很容易地转换为静态帮助函数)从读取器获取可能为空的值:
public static T Get<T>(this IDataReader reader, string columnName)
{
if (reader[columnName] == DbNull.Value)
{
return default(T);
}
return (T)reader[columnName];
}
public static T Get(此IDataReader读取器,字符串列名)
{
if(读卡器[columnName]==DbNull.Value)
{
返回默认值(T);
}
返回(T)读卡器[列名称];
}
要想在不支付太多运行时开销的情况下实现这一点,恐怕你不能太挑剔
但是,还有一种更简洁的语法:
public static Nullable<T> GetNullableValue<T>(this IDataRecord record,
int columnIndex, Func<int, T> getValue)
where T: struct;
{
if (record.IsDbNull(columnIndex))
return null;
else
return getValue(columnIndex);
}
var xyz = reader.GetNullableValue(0, reader.GetDecimal);
公共静态Nullable GetNullableValue(此IDataRecord记录,
int columnIndex,Func getValue)
其中T:struct;
{
if(记录IsDbNull(列索引))
返回null;
其他的
返回getValue(columnIndex);
}
var xyz=reader.GetNullableValue(0,reader.GetDecimal);
IsDbNull(int)通常比使用GetSqlInt32之类的方法,然后与DBNull.Value或使用它自己的.IsNull之类的方法慢得多:
public static int Int32(this SqlDataReader r, int ord)
{
var t = r.GetSqlInt32(ord);
return t.IsNull ? default(int) : t.Value;
}
尝试了一些模板解决方案,但到目前为止没有任何效果。问题是,所有Sql类型(这里是SqlInt32)实际上都是结构,而它们都有.Value属性C#,没有真正的模板来处理它。此外,它们还有自己的INullable接口,该接口只有.IsNull,不能与Nyllable兼容
我怀疑需要一整套Sql类型作为C#模板,或者向它们添加ICOnvertible,以便只需要一个或两个模板化方法
如果有人有一个或两个功能性技巧的想法,请大声说出来:-)我对方法二的唯一问题是,对于JR程序员来说,它并不完全明显(毕竟,人们必须维护它);事实上,我们只在有值的时候赋值,而在其他情况下保持不变,这需要一秒钟的时间来理解。我知道这看起来很琐碎,但相信我,人们会对简单的东西感到困惑。你必须将其与使用类型转换和三元条件运算符进行权衡,我发现这至少同样令人困惑。那么只需显式地编写上面的代码,或者添加一条注释,说明非赋值意味着
null
。这使我认为Skeet的扩展方法的想法可能最有意义,因为它最明显、最可读。但我拒绝给他打勾,他得分太多了!嗯,这是有原因的,不是吗;)只需继续=)GetDecimal
返回decimal
,它永远不会是null
,因此?
不会工作。我看到你更新了你的帖子,但现在更糟了<代码>??null在逻辑上是错误的(如果它不是null
则返回值,如果它是null
,则返回null
,因为decimal
不能是null
),请参阅备注部分,如果它不是decimal+1,则应抛出InvalidCastException:聪明的可读函数。在我看来,在ea中定义这样一个静态助手方法甚至是值得的
public static int Int32(this SqlDataReader r, int ord)
{
var t = r.GetSqlInt32(ord);
return t.IsNull ? default(int) : t.Value;
}