为什么 TimeSpan 和 Guid Structs 可以与 null 进行比较?

发布于 2024-07-30 18:02:19 字数 961 浏览 6 评论 0原文

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

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

将编译得很好(与 Guid 结构相同)。
现在我知道结构是值类型,并且上面的代码不应该编译,除非有一个接受对象的运算符 == 的重载。 但是,据我所知,没有。
我已经使用 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 进行比较。

I've noticed that some .NET structs can be compared to null.
For example:

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

will compile just fine (the same with the Guid struct).
Now I know that stucts are value type and that the code above should not compile, unless there's an overload of operator == which takes an object. But, as far as I could tell there isn't.
I've looked at the class with Reflector, and also at the docs on MSDN.
The two of them do implement the following interfaces:

IComparable, IComparable<T>, IEquatable<T>

but, trying to implment the same Interfaces did not seem to help:

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

I'm using: .NET 2.0 Visual Studio 2005.

Does anyone has any idea what's the reason for this ?
I am just trying to get a better understanding. This isn't an issue as I know I shouldn't compare structs to null anyway.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

挽容 2024-08-06 18:02:20

这是 == 运算符。

TimeSpan 类重载了相等运算符:

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

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

随着可为空类型的到来,每个结构都可以隐式转换为其可为空类型,因此,当您看到类似

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

You don't see 的内容时,就表明这种情况正在发生:

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;

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

好吧, msdn 在这里有帮助,指出:

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

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

It's the == operator.

The TimeSpan class has an overload of the equality operator:

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

This in itself doesn't make it possible to compare with null, but...

With the arrival of nullable types, each struct is implicitly convertible to its nullable type, so when you see something like

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

You don't see that this is happening:

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

Null gets the implicit conversion (implicit assignment?), but not all System.Object objects do:

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

Okay, but the equality operator doesn't take nullable arguments, does it?

Well, msdn is of help here, stating:

The predefined unary and binary
operators and any user-defined
operators that exist for value types
may also be used by nullable types.
These operators produce a null value
if [any of] the operands are null; otherwise,
the operator uses the contained value
to calculate the result.

So you effectively get a nullable implementation for each operator for free, with a fixed defined behaviour. The "contained value" mentioned above is the actual value the non-nullable operator would return.

不顾 2024-08-06 18:02:20

当包含可空类型时,这个问题就被有效地引入了。 存在从 TimeSpanTimeSpan? 的隐式转换,并且 TimeSpan? 与该类型的 null 值之间存在比较。

编译器对某些类型发出警告,这使得它想要做什么更清楚:

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 和我计算出了一次发出警告的情况......遗憾的是它不一致。

This problem was effectively introduced when nullable types were included. There's an implicit conversion from TimeSpan to TimeSpan?, and there's a comparison between TimeSpan? and the null value of that type.

The compiler issues a warning for some types which makes it clearer what it's trying to do:

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

Gives this warning:

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?'

I believe Marc Gravell and I worked out the circumstances under which the warning is given once... it's a shame it's not consistent.

奢欲 2024-08-06 18:02:20

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

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

我仔细研究了规范,但找不到更通用的规则。 乔恩的答案表明它是可为空的促销问题。

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

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

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

然后反编译它,您将看到以下内容

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

This case is covered for generics in section 7.9.6 of the C# language specification.

The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.

I dug through the spec for a bit and couldn't find a more general rule. Jon's answer indicates it's a nullable promotion issue.

This rule (or a similar variation) does seem to be being applied here. If you look at the reflected output closely you'll notice the comparison isn't there. The C# compiler is apparently optimizing this comparison away and replacing it with false.

For instance, if you type the following

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

Then decompile it you'll see the following

var x = new TimeSpan();
var y = false;
Console.WriteLine(x);
若有似无的小暗淡 2024-08-06 18:02:20

另请参阅:C# 3 (.NET 3.5) 版本的 csc 无法报告CS0162 表示无法访问的代码 (struct/null)

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

因为 Guid / TimeSpan 等提供 ==,它们会落入这个陷阱,但不会警告您。

See also: C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)

Starting with the C# 3 compiler that means it sometimes doesn't even warn you about this ;-p

Because Guid / TimeSpan etc provide ==, they fall into this trap where it doesn't warn you.

此刻的回忆 2024-08-06 18:02:20

我找到了 :)

下面给出了一个警告:

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 类型?< /code> 进行比较。

编辑:规范中的相关部分是第 §13.7.1 节,指出 null 可以隐式转换为任何可为空类型,并且(很难阅读)第 §13.7.2 节指出值类型 < code>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'
    ...

I FOUND IT :)

The following gives a warning:

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?'

The compiler is just failing to emit the correct warning that the null you typed was converted to type TimeSpan? for the comparison.

Edit: The related section in the spec is §13.7.1 stating that null can be implicitly converted to any nullable type, and (the very difficult to read) section §13.7.2 stating a value type T can be implicitly converted to T?.

What I originally wrote:

Whatever's happening is something in the C# spec because like JaredPar says it compiles to simply false.

Note that this doesn't compile:

TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
    ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文