【问题标题】:Why compiler treats System.Object as null when its set to False为什么编译器将 System.Object 设置为 False 时将其视为 null
【发布时间】:2018-01-09 19:49:05
【问题描述】:

我编写了一个通用方法来从数据库 (MSSQL Server) 中检索单个值。
我遇到了一个需要从 DB 中获取 Boolean 值的案例。

正如您在下面的代码中看到的,Object 本地字段 (IsExist) 会得到结果。

当 DB 中的值为 False 时,GenericScalar() 方法返回 False(应该如此) 并且条件:GetWanLineDisconnectionData() 中的 if (IsExist == null) 为真,并且正在执行返回块,即使 IsExist 为假且不为空。

这是为什么呢?

我该如何克服这个问题?

private void GetWanLineDisconnectionData()
{
    string q = "SELECT WanLineDiscconection FROM AdditionalProjectsData WHERE SpCall= " + "'" + spCall + "'";
    object IsExist = Orange.ProjectManagment.DAL.Database.GenericScalar<object>(q);
    if (IsExist == null) {
        return;
    }
    if (bool.Parse(IsExist) == true) {
        RadWanDiscYes.Checked = true;
    } else {
        RadWanDiscNo.Checked = true;
    }    
}

数据库方法:

public static T GenericScalar<T>(string query)
{
    OleDbConnection connection = new OleDbConnection(sqlConnString);
    connection.Open();

    OleDbCommand cmd = new OleDbCommand(query, connection);
    try
    {
        var result = cmd.ExecuteScalar();

        if (result == null)
        {
            return default(T);
        }
        else
        {
            return (T)result;
        }
    }

    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        CloseConnection(ref connection);
    }
}

编辑:
也许一些屏幕截图会更好地展示它: (注意:GetWanLineDisconnectionData() 是用 VB.NET 编写的,GenericScalar() 是在解决方案中的不同项目上用 C# 编写的):

  1. 一开始IsExist 什么都不是(空)。
  2. 查询找到一行,bool 列 WanLineDiscconection 的值为 false,IsExist 应设置为 false。

  3. 问题出在这里,程序进入 if 块,IsExist 不是空的(null)。

【问题讨论】:

  • 它的 null 表示没有值,但 DBNull.Value 表示 db NULL btw。
  • 请使用using 块,您的sn-p 是教科书示例,说明如何自己动手搞砸。不要throw ex;。仅使用throw;,替代方案会破坏堆栈跟踪。
  • WanLineDiscconection的类型是什么?
  • @nvoigt 是的,但在这种情况下,最好完全省略 catch 块,因为它没有任何功能(除了破坏堆栈跟踪)。
  • @HimBromBeere 它的布尔值

标签: c# .net vb.net generics


【解决方案1】:

object foo = false中的变量foo肯定不是null,所以你标题中的前提不正确。

ExecuteScalar() 在没有行时返回null。在这种情况下,方法GenericScalar() 返回default(T),对于object 将是null

如何解决这个问题取决于您的数据是什么样的。您可能希望将结果表示为可为空的 int 或 int? 而不是 object

var exists = Orange.ProjectManagment.DAL.Database.GenericScalar<int?>(q);
RadWanDiscYes.Checked = exists.GetValueOrDefault() > 0;

请参阅How does GetValueOrDefault work?What is the default value of the nullable type "int?" (including question mark)?

除此之外:您通常也不想为数据库查询编写方便的包装器,因为您重新发明轮子很糟糕,将您的应用程序开放给 SQL 注入。话虽如此,该方法还有很多问题,包括但不限于不释放数据库连接和在没有堆栈跟踪的情况下重新抛出异常。

【讨论】:

  • 为什么不使用exists.HasValue?似乎比查询该值并确定该值是否大于零更容易,不是吗?
  • @HimBromBeere 如答案所述,我不知道他们的数据是什么样的。 (int?)0 有一个值,但可能意味着 false
  • @CodeCaster 感谢您的回复和代码审查说明,但是有一行,布尔列WanLineDiscconection 的值为false,因此IsExist 值为false(非空)它是查询的预期结果,问题出在GetWanLineDisconnectionData() 方法内部:条件(IsExist == null) 将其视为null 而不是应有的错误,并且执行了if 块(并且不应执行) ,我知道这听起来很奇怪,我已经测试了几次,我不明白为什么。
  • 另一件可能很重要的事情,GetWanLineDisconnectionData() 是用 VB.NET 编写的,GenericScalar() 是在解决方案中的不同项目上用 C# 编写的。
  • @jonathana 您可能正在调试发布版本,而调试器在离开方法的那一行撒谎。
【解决方案2】:

您正在混合编译-time 和runtime 信息。虽然GenericScalar&lt;object&gt;(q) 是编译时存在的信息,但在编译时从ExecuteScalar 返回的类型只是object。您希望它是boolean 类型,这对于您的特定情况可能是正确的,也可能不是。但是,这与编译器无关。

简而言之,这意味着T 实际上是object,而不是您期望它来自数据库的任何内容。正如 CodeCaster 已经提到的那样,object 的默认值就是 null

我建议使用GenericScalar&lt;bool&gt; 而不是GenericScalar&lt;object&gt;,因为您似乎知道您的查询实际返回什么——在您的情况下是bool。然后default(T) 计算为false,而不是null

【讨论】:

  • 感谢您的回复,我已经尝试过使用 bool 类型。请参阅我对 CodeCaster 答案的评论。
猜你喜欢
  • 2021-11-22
  • 2021-12-06
  • 1970-01-01
  • 2017-04-16
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多