【问题标题】:Why can TimeSpan and Guid Structs be compared to null?为什么 TimeSpan 和 Guid Structs 可以与 null 进行比较?
【发布时间】:2009-08-04 06:36:17
【问题描述】:

我注意到一些 .NET 结构可以与 null 进行比较。 例如:

  TimeSpan y = new TimeSpan();
        if (y == null)
            return;

会编译得很好(与 Guid 结构相同)。
现在我知道结构是值类型并且上面的代码不应该编译,除非有一个重载的 operator == 需要一个对象。但是,据我所知没有。
我已经使用 Reflector 查看了该课程,还查看了 MSDN 上的文档。
他们两个确实实现了以下接口:

IComparable, IComparable<T>, IEquatable<T>

但是,尝试实现相同的接口似乎没有帮助:

struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
    public int CompareTo(Object obj) {
        return 0;
    }
    public int CompareTo (XX other){
        return 0;
    }
    public bool Equals (XX other){
        return false;
    }
    public override bool Equals(object value){
        return false;
    }
    public static int Compare(XX t1, XX t2){
        return 0;
    }
}

我正在使用:.NET 2.0 Visual Studio 2005。

有人知道这是什么原因吗? 我只是想更好地理解。这不是问题,因为我知道我不应该将结构与 null 进行比较。

【问题讨论】:

  • “我正在使用:.NET 2.0 Visual Studio 2005。” - 它会发出警告吗? IIRC,它应该在 2005 年(但不是 2008 年,见下文)
  • TimeSpan 没有警告,XX 有错误:错误 CS0019:运算符“==”不能应用于“TransControl.TraceDisplay.XX”和“”类型的操作数

标签: c# .net .net-2.0 null struct


【解决方案1】:

这是== 运算符。

TimeSpan 类具有等式运算符的重载:

public static bool operator ==(DateTime d1, DateTime d2)
{
     return (t1._ticks == t2._ticks);
}

这本身并不能与null进行比较,但是...

随着可空类型的到来,每个结构都可以隐式转换为其可空类型,所以当你看到类似的东西时

TimeSpan y = new TimeSpan();
if (y == null)
    return;

没有看到这正在发生:

TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
    return;

Null 获取隐式转换(隐式赋值?),但并非所有System.Object 对象都这样做:

TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
    return;

好的,但是相等运算符不接受可为空的参数,是吗?

好吧,msdn 在这里有帮助,说明:

预定义的一元和二元 运算符和任何用户定义的 存在于值类型的运算符 也可以由可为空的类型使用。 这些运算符产生一个空值 如果 [任何] 操作数为空;除此以外, 运算符使用包含的值 计算结果。

因此,您可以有效地获得每个运算符的可空实现免费,并具有固定定义的行为。上面提到的“包含值”是不可为空的运算符返回的实际值。

【讨论】:

  • 你是对的!这是操作员==!现在将它添加到我的自定义结构中将允许我与 null 进行比较!
  • 是的。为了清楚起见,也许我应该在帖子的开头添加这句话:)
【解决方案2】:

当包含可空类型时,有效地引入了这个问题。有一个从TimeSpanTimeSpan? 的隐式转换,还有TimeSpan? 和该类型的空值之间的比较。

编译器会针对某些类型发出警告,以便更清楚地了解它正在尝试做什么:

int x = 10;
if (x == null)
{
    Console.WriteLine();
}

给出这个警告:

Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
       since a value of type 'int' is never equal to 'null' of type 'int?'

我相信 Marc Gravell 和我已经计算出发出警告一次的情况......很遗憾,它不一致。

【讨论】:

  • 该死,你打败了我 ;-p 对于任何自定义结构(是否使用 Equals?==?)它会静默(没有警告)注意到它是多余的。 IIRC 它在 C# 2.0 和 C# 3.0 之间发生了变化;想试试 C# 4.0 吗?它可能已被重新修复...
  • @Marc,我在 VS2010 Beta1 上尝试了 OP 的重现,它重现了。没有尝试过很多其他案例
  • @JaredPar - 是的,当我发现我发布的连接问题时,Ed Maurer 说他不会修复它。抱歉,我 2010 年的评论早于我找到这篇文章的时间;-p
  • 这里肯定有人可以归咎于:deal:
  • @Marc- 我查看了您的连接问题:connect.microsoft.com/VisualStudio/feedback/… 我认为这可能对其他人有益。 Ed Maurer 说以前的版本中曾经有过警告,但正如我提到的,我在 VS2005 中没有得到警告
【解决方案3】:

C# 语言规范的第 7.9.6 节中涵盖了泛型的这种情况。

即使 T 可以表示值类型,也允许 x == null 构造,并且当 T 是值类型时,结果被简单地定义为 false。

我仔细研究了规范,找不到更通用的规则。 Jon 的answer 表明这是一个可以为空的促销问题。

此规则(或类似的变体)似乎确实适用于此。如果您仔细查看反射输出,您会发现比较不存在。 C# 编译器显然正在优化这种比较并将其替换为 false。

例如,如果您键入以下内容

var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);

然后反编译你会看到如下

var x = new TimeSpan();
var y = false;
Console.WriteLine(x);

【讨论】:

  • 其实编译器应该在这里发出警告,如果它替换了比较。至少,在其他情况下是这样。神秘。
  • 有趣的是,如果将其放入比较中... if( x == null) 您将收到针对 int 类型的警告,而不是针对 guid 类型的警告。也许有一种秘密的方法可以使 Guid 无效。 :)
  • 我发布的连接问题涵盖的自定义结构。
【解决方案4】:

另请参阅:C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)

从 C# 3 编译器开始,这意味着它有时甚至不会警告您;-p

因为Guid / TimeSpan 等提供==,所以他们掉进了这个不会警告你的陷阱。

【讨论】:

    【解决方案5】:

    我找到了 :)

    以下给出警告:

    int i = 0;
    if (i == null)
    // ^^ Warning: The result of the expression is always 'false' since a value of
    //             type 'int' is never equal to 'null' of type 'int?'
    

    编译器只是未能发出正确的警告,即您键入的 null 已转换为类型 TimeSpan? 以进行比较。

    编辑:规范中的相关部分是 §13.7.1 说明 null 可以隐式转换为任何可为空的类型,并且(非常难以阅读)部分 §13.7.2 说明值类型 T可以隐式转换为T?

    我最初写的:

    无论发生什么都在 C# 规范中,因为就像 JaredPar 所说,它编译为简单的 false

    请注意,这不会编译:

    TimeSpan ts = new TimeSpan();
    object o = null;
    if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
        ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-25
      • 1970-01-01
      • 2018-07-12
      • 2010-12-30
      • 1970-01-01
      • 2014-10-11
      • 2013-05-04
      • 1970-01-01
      相关资源
      最近更新 更多