【问题标题】:Test for null values in C#在 C# 中测试空值
【发布时间】:2011-09-10 23:26:16
【问题描述】:

如果我这样做:

DataSet ds = GetMyDataset();

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

有没有不使用 try/catch 来检查空值的好方法?只是我不在乎“col1”中的值是否为空,或者“col1”不存在,或者没有返回的行,或者表不存在!

也许我应该关心? :) 也许 try/catch 是解决这个问题的最佳方法,但我只是想知道是否还有其他方法可以做到这一点?

谢谢!

【问题讨论】:

  • 首先,不要做一个通用的catch。这是一种可怕的做法。捕捉您正在寻找的错误。
  • 同意。它只是作为示例抛出,但它有点相同 - “不要使用错误处理来捕获不是真正错误的东西”。我只是在寻找与此问题相关的任何 C# 技术/关键字。
  • Maybe monad 在这里似乎是个好东西。 codeproject.com/KB/cs/maybemonads.aspx

标签: c# dataset


【解决方案1】:

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

例如,期待table[0].Rows.Count == 0 是一种更正常的做法。

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

【讨论】:

  • 我想是的。只是即使你写了所有代码“如果有一个数据集,如果有一个表,如果有一个 ROW,如果有一个列”,那么最终结果仍然是你只是真的感兴趣在获得价值与否!肯定有办法增加代码的可读性吗?
  • "增加代码可读性" - 更好的设计,可能更少的 DataSet。您的查询应始终生成(某种程度上)定义的类型,因此可以避免丢失表和列。
  • 这只是一个例子。我们都遇到过嵌套对象的情况(不一定是数据集),如果 obj !=null && obj.obj1 != null && obj.obj1.obj2 != null 然后得到 obj.obj1。 obj2.value。对吗?
  • @jqwha,没有捷径可走。经常请求?. 形式的运算符,您可以说a?.b,如果a 为null,则不会抛出,输出将被定义为null 或者可能是该类型的默认值的b。但这不是目前语言的一部分,我也不知道它是否有可能在未来的某个版本中出现。
  • 是的,它发生了。但当它频繁出现或超过 2 层时,我确实开始回顾设计。
【解决方案2】:
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...
...

【讨论】:

  • 可能以if (world == null || ...开头
  • 哈哈。他们确实需要在语言中构建一些递归的东西。
【解决方案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"];
}

【讨论】:

    【解决方案4】:
    DataSet ds = GetMyDataset();
    
    string somevalue = ds != null ? ds.Tables[0].Rows[0]["col1"].ToString() : null;
    

    【讨论】:

      【解决方案5】:

      您也许可以使用 nullabel terinary '??',但我认为返回的 null 是 'DBNull' 而不是 'null'。

      这个例子是……

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

      【讨论】:

      • 你说得对,这里还不够。那是空合并运算符,而不是“三元”或三元。大家都叫三元的是条件运算符,形式为a ? b : c
      【解决方案6】:

      做一些检查:

      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();
                      }
                  }
              }
      

      【讨论】:

        【解决方案7】:

        我认为May be Monad 最适合这种情况(示例来自源代码):

        public static TResult With<TInput, TResult>(this TInput o, 
               Func<TInput, TResult> evaluator)
               where TResult : class where TInput : class
        {
          if (o == null) return null;
          return evaluator(o);
        }
        
        string postCode = this.With(x => person)
                              .With(x => x.Address)
                              .With(x => x.PostCode)
        

        你的情况类似于:

        ds.With(x=>Tables[0]).With(x=>x.Rows).With(x=>x[0])...
        

        您将创建一个扩展类并使用它而无需担心空引用,此外Return 方法对您查看链接很有用。

        【讨论】:

        • 你能详细说明你是如何让它与数据集一起工作的吗?试图做类似的事情,但它打破并给了我一个".. does not belong to the table Table",当我尝试这个时:var test = daVideos.With(e =&gt; e.Tables[0]).With(e =&gt; e.Rows).With(e =&gt; e[0]).With(e=&gt;e["Description"]).ToString();
        【解决方案8】:

        您必须从顶部开始(即 DataSet)一一检查它们是否为 null(或其他 null 类型)

        如果(ds!= null) 如果(表!=空) ...

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

        【讨论】:

        • 是的,但请参阅上面我对 Henk 的评论。
        【解决方案9】:

        从维护的角度来看,您所做的事情是非常可取的。

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

        肯定会省去很多繁琐的代码。

        【讨论】:

          【解决方案10】:

          我过去使用的是一个返回默认值的 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 应该在您确定行首先存在之后执行。
          【解决方案11】:

          我认为您应该明确表示您不关心所有这些事情,这意味着您应该明确处理每种情况:

          if (ds.Tables.Count == 0)
            return null;
          
          var table = ds.Tables[0];
          if (table.Rows.Count == 0)
            return null;
          
          if (!table.Columns.Contains("col1"))
            return null;
          
          var row = ds.Rows[0];
          if (row.IsNull("col1"))
            return null;
          
          return row["col1"]
          

          那是更多的代码,但对我来说它更清楚地传达了意图。

          【讨论】:

          • 但“意图”是检索一个值。我认为我们不应该指定所有我们不关心的事情。我不是黑客之类的,但我认为有一个平衡点?
          • 我认为其目的是在处理一堆特殊情况时获取价值,“特殊”是这里的关键字。不关心这些事情在这里很重要,因为这不常见。当然,您可以(并且可能应该)将其放入一个方法中并重用它,从而减少额外代码的开销。
          【解决方案12】:

          如果你真的发送随机的、未经验证的数据集、索引和名称,你可以使用这样的辅助方法:

          public static Object GetDataSetValue(DataSet dataSet, int tableIndex, int rowIndex, string columnName)
          {
              Object value = null;
          
              if (dataSet != null
                  && tableIndex >= 0
                  && tableIndex < dataSet.Tables.Count
                  && rowIndex >= 0
                  && rowIndex < dataSet.Tables[tableIndex].Rows.Count
                  && dataSet.Tables[tableIndex].Columns.Contains(columnName))
              {
                  value = dataSet.Tables[tableIndex].Rows[rowIndex][columnName];
              }
          
              return value;
          }
          

          然后只需使用类似GetDataSetValue(ds, 0, 0, "col1") 的东西。

          【讨论】:

          • 您建议的替代方案是什么,可以涵盖所需数据访问的所有可能性?
          • @jqwha 将方法更改为您会考虑所有可能性的方法,然后调用它,我尝试包括您提到的所有可能性,但您可以使用该想法来实现自己的想法。
          【解决方案13】:

          我讨厌不得不处理不能保证获得标准结果集的数据访问层:任何给定的 SQL 查询或存储过程都应该始终返回相同的结果集架构。处理空集(又名 DataTables)很容易。处理任意丢失的对象引用...不是那么多。

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

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

          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 个值的集合,您可以很容易地看到存在的内容和缺少的内容。它可以很容易地看出违反了哪些假设(约束?)。

          【讨论】:

            【解决方案14】:

            您检查 DBNull.Value 例如

            if (ds.Tables[0].Rows[0]["col1"] != DBNull.Value)
            

            【讨论】:

            • 问题:空数据集、无表、无行等
            猜你喜欢
            • 2019-10-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-01-02
            • 1970-01-01
            • 2022-08-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多