【问题标题】:? Constructor handling with nullable types?可空类型的构造函数处理
【发布时间】:2013-08-13 16:46:31
【问题描述】:

小问题,仅供理解:我有 2 个可为空的日期时间。我读出了创建时间和更新时间,都可以填写。所以我想检查一下哪个是以后的:

lastChangedIndrole = (tmpCreate > tmpUpdate ? tmpCreate : tmpUpdate);

但是这里发生了一些奇怪的事情。我希望在 f.e. 时抛出一个错误。 tmpUpdate 为空,但它似乎返回了一些东西,但不是正确的值,而是第二个值,在我的示例中是更新。

有什么我不明白的吗?我认为代码会检查毫秒到 1900,如果有空值,则会引发错误。但这不会发生。这是我不懂的魔法吗?

附: : 有什么特别的词吗?像vb中的IIF这样的构造函数?很难搜索。

谢谢,本周有个好的开始

马蒂亚斯

【问题讨论】:

  • 为什么您会期望这会引发异常?请注意,您正在谈论的代码中没有构造函数。您的意思是“条件运算符”而不是“构造函数”吗?
  • 您可能也有兴趣检查值是否为null 或等于DateTime.MinValue,在将操作结果分配给另一个变量之前确保数据有效很重要(导致另一个奇怪的行为且难以调试)。
  • 我认为您的意思是使用“construct”这个词,而不是“constructor”,因为后者在 .NET 和 C#(以及许多编程语言)中具有特殊含义,而“construct” " 是指由较小的部分组合而成的东西,或由组合术语和想法构成的陈述/句子。
  • 我没有写完整的代码。我的问题就像:当代码检查一个可空值的值并且它为空并且它检查这个值是否大于另一个值时,他是如何处理它的?由于检查 null ref 类型会引发错误,我希望与可为 null 的类型相同?

标签: c# datetime nullable


【解决方案1】:

Nullable.Value 读取时,可空类型为空会引发异常。但是它不会在比较时抛出异常,但会给出意想不到的结果(比较null与非空值永远不会返回true)。

下面的sn-p会说明问题

DateTime? dt1 = null;
DateTime? dt2 = DateTime.Now;

bool b1 = dt2 > dt1;//false(we expect true here)
bool b2 = dt2 < dt1;//false
bool b3 = dt2 == dt1;//false

此行为已记录在 Here

当您对可空类型执行比较时,如果一个可空类型的值为 null 而另一个不是,则所有比较的结果都为 false,但 !=(不等于)除外。重要的是不要假设因为特定的比较返回 false,相反的情况返回 true。在以下示例中,10 不大于、小于或等于 null。只有 num1 != num2 的计算结果为真。

【讨论】:

    【解决方案2】:

    C# 将 &lt;&gt; 运算符提升到可空类型之上,如果参数之一为空,它们将返回 false

    因此,如果tmpCreatetmpUpdate 为空,则tmpCreate &gt; tmpUpdate 的计算结果为false

    规范中有描述:

    7.3.7 提升运营商

    • 对于关系运算符 = 如果操作数类型都是不可为空的值类型,则运算符存在 如果结果类型是布尔值。提升的形式由 添加一个?每个操作数类型的修饰符。提升的操作员 如果一个或两个操作数为空,则生成值 false。否则, 提升的运算符展开操作数并应用底层 运算符来产生布尔结果。

    【讨论】:

    • ((int?)null) == ((int?)null) == true。我讨厌可以为空的比较 :-) 为它们建立真值表是令人头疼的程度。
    • @xanatos - 是的,== 是对的。我已将其删除,因为它与此问题无关。
    • 有趣...&lt;=&gt;= 完全解除了...所以null == null,但是!null &lt;= null
    【解决方案3】:

    您可以比较两个DateTime? 对象,但大多数情况下,当至少一个操作数为null 时,结果将为false

    例如:

    DateTime? today = DateTime.Today;
    DateTime? yesterday = DateTime.Today.AddDays(-1);
    DateTime? nodate = null;
    DateTime? nodate2 = null;
    
    Console.WriteLine(today > yesterday); //true
    Console.WriteLine(today < yesterday); //false
    
    Console.WriteLine(today > nodate); //false
    Console.WriteLine(today == nodate); //false
    Console.WriteLine(today < nodate); //false
    
    Console.WriteLine(nodate > yesterday); //false
    Console.WriteLine(nodate == yesterday); //false
    Console.WriteLine(nodate < yesterday); //false
    
    Console.WriteLine(nodate > nodate2); //false
    Console.WriteLine(nodate == nodate2); //true - this is the exception
    Console.WriteLine(nodate < nodate2); //false
    

    我建议避免过于聪明,并在代码中更加明确:

    if (tmpUpdate.HasValue)
    {
       if (tmpCreate.HasValue)
       {
           lastChangedIndrole = (tmpCreate > tmpUpdate ? tmpCreate : tmpUpdate);
       }
       else
       {
           lastChangedIndrole = tmpUpdate;
       }
    }
    else
    {
       if (tmpCreate.HasValue)
       {
           lastChangedIndrole = tmpCreate;
       }
       else
       {
           lastChangedIndrole = null;
       }
    }
    

    【讨论】:

    • 非常感谢,我只是在检查它们时确保这些值不为空,所以我不会遇到这个问题。
    【解决方案4】:

    当您将可空值与其他可空值或不可空值进行比较时,非可空类型上的运算符被“提升”,因此也适用于它们的可空对应物。

    但是,特殊处理将处理其中一个或两个都为 null 的情况。

    这是一个LINQPad 示例:

    void Main()
    {
        int? a = 10;
        int? b = null;
    
        (a > b).Dump();
        (b > a).Dump();
        (a == b).Dump();
        (a != b).Dump();
    }
    

    输出:

    False
    False
    False
    True
    

    如您所见,在比较两个可为空的 int(其中一个是 null)时,只有相等运算符会产生预期的结果。

    如果我们将 a 变量设为不可为空的 int:

    int a = 10;
    

    但否则保留代码,那么它会产生完全相同的结果。

    如果两者都为空怎么办?

    int? a = null;
    int? b = null;
    

    生产:

    False
    False
    True
    False
    

    结论:

    • 等式运算符(==!=)正确处理可空类型的 nulls
    • 小于或大于运算符不会,如果操作数之一是null,它们将返回false,即使您切换比较。基本上,10 既不小于也不大于 null

    如果尝试读取null可为空的类型值的.Value属性,会抛出异常,但运算符不会直接去.Value属性,而是先检查.HasValue属性,然后在尝试实际比较之前处理这些情况。

    【讨论】:

      【解决方案5】:

      使用^ (xor) 检查一个条件是否为真(等于空),然后?? 返回第一个非空值。如果两者都不为空,请使用您现有的表达式。

      if (tmpCreate == null ^ tmpUpdate == null)
         lastChangedIndrole = tmpCreate ?? tmpUpdate;
      else
         lastChangedIndrole = (tmpCreate > tmpUpdate ? tmpCreate : tmpUpdate); 
      

      但您也可以选择直接分配第一个非空值,然后如果 tmpUpdate 大于该值,则覆盖它:

      lastChangedInRole = tmpCreate ?? tmpUpdate; 
      if (tmpUpdate > lastChangedInRole) 
        lastChangedInRole = tmpUpdate;
      

      (原理:如果只有一个有值,比较总是假的,非空值将使用??分配,否则tmpCreated将被分配,只需将其与@进行比较987654327@.)

      【讨论】:

        【解决方案6】:

        使用 ^ (xor) 检查是否恰好有一个条件为真(等于 null)然后 ??返回第一个非空值。如果两者都不为空,请使用您现有的表达式。

        如果(tmpCreate == null ^ tmpUpdate == null) lastChangedIndrole = tmpCreate ?? tmp更新; 别的 lastChangedIndrole = (tmpCreate > tmpUpdate ? tmpCreate : tmpUpdate);

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-28
          • 2011-10-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多