在 C# 中测试空值

12

如果我做了这样的事情:

DataSet ds = GetMyDataset();

try
{
    string somevalue = ds.Tables[0].Rows[0]["col1"];
}
catch
{
    //maybe something was null
}

有没有一种好的方法可以在不使用 try/catch 的情况下检查空值?我的意思是,如果“col1”中的值为null,或者“col1”不存在,或者没有返回行,或者表不存在,我都不关心!

也许我应该在意吗?:) 也许 try/catch 是处理这个问题的最佳方式,但我只是想知道是否还有其他方法可以解决它?

谢谢!


9
首先,不要使用通用的catch语句,这是一种糟糕的做法。捕获你正在寻找的错误类型。 - Chris Eberle
同意。这只是作为一个例子被提出来,但它其实是相同的观点 - “不要使用错误处理来捕获那些不是真正错误的东西”。我只是在寻找与此问题相关的任何C#技巧/关键字。 - jqwha
2
Maybe Monad 在这里似乎是个好东西。http://www.codeproject.com/KB/cs/maybemonads.aspx - JWL_
14个回答

7

不关心表格或列是有点奇怪的。

比如,期望 table[0].Rows.Count == 0 是一种更加正常的实践。

检查 NULL 值的最佳方式是使用 if(...) ... else ...。最糟糕的方式是等待异常(以任何方式)。


1
"增加代码可读性" - 更好的设计,也许少用 DataSet。你的查询应该总是产生一个(有点)定义明确的类型,这样就可以避免缺失表和列。 - H H
这只是一个例子。我们都遇到过这种情况(不一定是与数据集有关),其中您有嵌套对象并且必须说如果 obj!= null && obj.obj1!= null && obj.obj1.obj2!=null,则获取obj.obj1.obj2.value。对吧? - jqwha
是的,这种情况确实会发生。但如果它频繁出现或超过2个层级深度,我就会开始回顾设计。 - H H
@Anthony - 我也是这么想的 :(. 谢谢。 - jqwha
@Henk - 我知道你在暗示什么,但我从来不相信“这是你的设计”的评论。纯粹等于简单实在是太过学院派了,事实上追求学术上纯粹的设计往往会导致大大增加复杂性。 - jqwha
显示剩余5条评论

4
if (ds == null
    || ds.Tables == null 
    || ds.Tables.Count == 0
    || ds.Tables[0].Rows == null 
    || ds.Tables[0].Rows.Count == 0
    || ds.Tables[0].Rows[0].IsNull("col1")
)
//there is no data...
...

2
也许可以从 if (world == null || ... 开始。 - H H
1
哈哈,他们真的需要在语言中构建一些递归的东西。 - jqwha

3

如果你想确保你的代码不会出错,实际上你需要检查层级中的所有元素,例如:

string someValue = "";

if (ds != null &&
    ds.Tables != null &&
    ds.Tables.Any() &&
    ds.Tables[0].Rows != null &&
    ds.Tables[0].Rows.Any() &&
    ds.Tables[0].Rows[0]["col1"] != DBNull.Value)
{
    someValue = ds.Tables[0].Rows[0]["col1"];
}

2
DataSet ds = GetMyDataset();

string somevalue = ds != null ? ds.Tables[0].Rows[0]["col1"].ToString() : null;

1

你可能可以使用可空三元运算符 "??", 但我认为返回的 null 是一个 DBNull 而不是 null。

例子如下...

string somevalue = ds.Tables[0].Rows[0]["col1"] ?? "";

你说得对,在这里使用它是不够的。那个操作符叫做空值合并运算符,不是“三元”的意思。大家所说的“三元”是条件运算符,形式为 a ? b : c - Anthony Pegram

1
进行一些检查:
string somevalue = String.Empty;
if (ds.Tables.Count > 0)
        {
            System.Data.DataTable dt = ds.Tables[0];
            if (dt.Rows.Count > 0)
            {
                System.Data.DataRow dr = dt.Rows[0];
                if (dt.Columns.Count>0 && dt.Columns.Contains("col1"))
                {
                   somevalue = dr["col1"].ToString();
                }
            }
        }

0

你必须一个接一个地检查它们是否为 null(或其他 null 类型),从最上面(也就是 DataSet)开始。

if (ds != null) if (table != null) ...

你可以不这样做,但你的代码会更容易出错。


是的,但请看我上面对Henk的评论。 - jqwha

0

从维护的角度来看,你所做的是非常理想的。

否则,你必须:检查 DS 是否为空,检查 DataSet 中是否有任何表,检查表中是否有任何行,检查列是否存在,检查列中是否有任何数据。

这肯定会消除很多繁琐的代码。


0

我讨厌处理没有保证标准结果集的数据访问层:任何给定的SQL查询或存储过程应该始终返回相同的结果集模式。处理空集(又称数据表)很容易。但是处理任意缺失对象引用就不那么容易了。

正确的答案是修复您的数据访问代码,使其返回一致的模式。

如果无法做到这一点,如果我必须处理这样的代码,我会像这样做:

DataSet   ds         = ExecuteStoredProcedure();
DataTable dt         = ( ds != null && ds.Tables != null ? ds.Tables[0] : null ) ;
DataRow   dr         = ( dt != null && dt.Rows   != null ? dt.Rows[0]   : null ) ;
object    o          = ( dr != null ? dr["someColumn"]) : null ) ;
string    someColumn = (string) colName ;

通过调试器或记录日志进行调试都很容易。给定一组5个值,您可以轻松地看到哪些是存在的,哪些是缺失的。这使得很容易看出哪些假设被违反了。


0
过去我使用的是一个小型的包装器,用于 Null 测试并返回默认值。例如:
    /// <summary>
    /// Test DBValue for DBNull and return NullReplaceValue if DBValue is DBNull
    /// </summary>
    /// <returns>Returns NullReplaceValue if DBValue is DBNull</returns>
    public static string NullStr(object DBValue, string NullReplaceValue)
    {
        if (object.ReferenceEquals(DBValue, DBNull.Value)) {
            return NullReplaceValue;
        } else {
            return Convert.ToString(DBValue);
        }
    }

正在使用中:

string somevalue = MyNullTests.NullStr(ds.Tables[0].Rows[0]["col1"], "Value was null");

正如其他人所说,要先确保行存在后才能执行字符串somevalue。 - Ed Charbeneau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接