编译器不应该允许Guid == null

时间:2010-02-01 15:41:46

标签: c# .net .net-3.5 guid

  

下面描述的行为仅针对.net-3.5

我刚刚遇到了C#编译器中最令人惊讶的行为;

我有以下代码:

Guid g1 = Guid.Empty;
bool b1= (g1 == null);

嗯, Guid不可为空,因此永远不能等于null 。 我在第2行中进行的比较始终返回false

如果对整数做同样的事情,编译器会发出警告,说结果总是为假:

int x=0;
bool b2= (x==null);

我的问题是:为什么编译器允许您将Guid与null进行比较
据我所知,它已经知道结果总是错误的 内置转换是否以编译器假定null为可能值的方式完成? 我在这里错过了什么吗?

4 个答案:

答案 0 :(得分:81)

马克是对的。定义自己的相等运算符的值类型自动获得定义的可升级版本,免费。采用两个可空guid的可空等式运算符适用于这种情况,将被调用,并且将始终返回false。

在C#2中,这会产生一个警告,但由于某种原因,这会停止为guid-to-null发出警告,但会继续产生int-to-null的警告。我不知道为什么;我还没来得及调查。

我为错误道歉;在重写C#3中的可空逻辑时,我可能搞砸了一个警告检测代码路径。在语言中添加表达式树主要改变了可空算术运算的实现顺序;我犯了许多错误移动代码。这是一些复杂的代码。

答案 1 :(得分:13)

比较有效,因为编译器会将Guid转换为Nullable<Guid>,然后才有意义。

有关未发出警告的错误报告here

有关Eric Lippert的更全面解释,请参阅 here here

答案 2 :(得分:1)

实际上,当Guild == null将返回true时会出现情况。

但是有点难以解释。

在ORM映射框架(例如openAccess)中,如果你有一个Guid字段,其默认值为Guid.Empty,当然可以有以下情况:

  • 您添加了新的Guid字段+属性
  • 升级旧数据库架构..在这种情况下,数据库中的所有值都将为NULL。
  • 如果你填充一个具有类型为Guild的空列的对象,当然如果你使用LINQ查询,对象将获得一个Guid.Empty值...在LINQ查询中它看起来Guid尚未填充,所以你需要使用== null。也许这是一个错误,但这是它的方式。

简而言之(使用OpenAccess,但可能不仅仅是):

var item = GetItems()。其中​​(i =&gt; i.SomeGuidField == null); 会起作用,你会得到物品 使用null guid这是在模式更新之后。 item.First()。SomeGuidField 将返回Empty Guid

var item = GetItems()。其中​​(i =&gt; i.SomeGuidField == Guid.Empty); 即使在项目的填充之后也将无法使用Guid.Empty并将返回空结果。

答案 3 :(得分:0)

当然,这不仅是Guid的问题。任何struct类型都会出现相同的行为,这种类型不是预定义的C#类型,前提是struct以通常的方式重载operator ==。框架中的其他示例包括DateTimeTimeSpan

这值得编译时警告,因为虽然技术上合法,因为提升的运算符,这不是一个有用的比较,因为它总是给出false。因此,它表明了程序员的错误。

正如Eric Lippert在他的回答中所说,编译时警告存在于Visual C#2.0编译器中。在版本3.0到5.0中,意外省略了警告(对于这些“用户定义的”struct类型,但不是针对int等预定义值类型,而不是针对枚举类型。

自C#6.0(基于Roslyn)以来,编译器再次检测到此代码问题。但是,由于向后兼容性(?!),除非使用所谓的严格功能编译代码,否则不会发出警告。

要在使用.csproj文件时启用strict(最常见的情况),请从Visual Studio卸载项目,编辑文件,插入XML元素:

<Features>strict</Features>

<PropertyGroup>文件的每个.csproj(通常会有一个以上)。然后,您会收到警告(如果您将处理警告用作错误,则可以“提升”为错误。)

如果您无法编辑.csproj,并且从命令行调用msbuild.exe进行编译,请使用开关:

/p:Features=strict

msbuild.exe

如果因为使用.csproj(C#编译器)直接编译而不使用csc.exe文件,请使用开关:

/features:strict

命令行上的csc.exe