50

The behaviour described below is specific to .net-3.5 only

I just ran across the most astonishing behavior in the C# compiler;

I have the following code:

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

Well, Guid is not nullable therefore it can never be equal to null. The comparison i'm making in line 2 always returns false.

If you make the same thing for an integer, the compiler issues an warning saying the result will always be false:

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

My question is: Why does the compiler lets you compare a Guid to null?
According to my knowledge, it already knows the result is always false.
Is the built-in conversion done in such a way that the compiler does assume null is a possible value?
Am I missing anything here?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Luis Filipe
  • 8,488
  • 7
  • 48
  • 76

4 Answers4

82

Mark is correct. Value types that define their own equality operators automatically get lifted-to-nullable versions defined as well, for free. The nullable equality operator that takes two nullable guids is applicable in this situation, will be called, and will always return false.

In C# 2, this produced a warning, but for some reason, this stopped producing a warning for guid-to-null but continues to produce a warning for int-to-null. I don't know why; I haven't had time to investigate yet.

I apologize for the error; I probably screwed up one of the warning-detection code paths when rewriting the nullable logic in C# 3. The addition of expression trees to the language majorly changed the order in which nullable arithmetic operations are realized; I made numerous mistakes moving that code around. It's some complicated code.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Is there a reason the behavior of Guids and strings are so different? Just implemented in different iterations instead of having the same implementation that a string can't be null and instead requires a nullable string instead and was never felt worthwhile for the breaking change? – Chris Marisic Feb 01 '10 at 16:22
  • @Chris - this glitch only impacts value-types. Strings are reference-types. `null` is (and always has been) perfectly valid for `string`. – Marc Gravell Feb 01 '10 at 16:37
  • 1
    @Chris, I have not had any Diet Dr. Pepper yet this morning, so I'm a bit slow. I cannot understand what you're asking. Are you asking why strings are reference types and guids are value types? – Eric Lippert Feb 01 '10 at 16:37
  • Really just goes back to I want `Nullable` in .NET because for all intensive purposes string is made to by used as a primitive type even though it's truly a reference type but it's the only primitive type that doesn't allow `Nullable` it's annoying to not be able to constrain whether null is a valid or invalid value of it and always having to do null checks where as every other object in my domain is guaranteed to be not null unless it's nullable and then I have GetValueOrDefault to leverage. – Chris Marisic Feb 01 '10 at 17:37
  • @Chris Marisic: `string` is not for all intents and purposes a primitive type. For example, `Frobber(s); // s is string` does not cause a copy of what `s` refers to to be made. The rest I don't understand; `string` is already "nullable" as it's a reference type. – jason Feb 01 '10 at 19:17
  • @Jason, he's saying he wants a *non nullable* string type. – Eric Lippert Feb 01 '10 at 19:54
  • @Eric Lippert: Oh! You're right; I was not reading that correctly at all. Thank you. – jason Feb 02 '10 at 01:44
  • 1
    Yes I believe that strings should behave the exact same way as Guids do. The fact they are immutable but still can be defined as null makes them some pseudo-primitive type that requires tons of custom coding to handle them which was clearly understood it would be that way when methods like `String.IsNullOrEmpty` exist and `String.Empty` exists. – Chris Marisic Feb 02 '10 at 17:51
  • 17
    You got to love StackOverflow. Where else can you raise a problem in a major framework and the author come back and say oops. – SamuelWarren Dec 09 '11 at 22:10
  • 1
    I just tested it with C#4.0 and the problem persists – Luis Filipe Jan 30 '12 at 10:35
  • @EricLippert the problem still exists? – Paul C Aug 21 '12 at 13:08
  • 1
    @Eric: Just remembered to test it with c#4.5 and the problem still persists... maybe in 5.0... – Luis Filipe Aug 23 '12 at 00:02
  • i had a proper guid nullable when i renamed it to guid but my lambda expression in which i was checking for null compiled fine, but then i found this post :) SO rocks (Y) – Ehsan Sajjad Jun 30 '16 at 12:30
13

The comparison is valid because the compiler converts the Guid to a Nullable<Guid> and then it makes sense.

There is a bug report on the warning not being issued here.

See here here for a fuller explanation from Eric Lippert.

Community
  • 1
  • 1
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
1

Actually there is a case when Guild == null will return true.

However it is kinda hard to explain.

In ORM mapping frameworks (openAccess for example) when you have a Guid field which will have a default value of Guid.Empty of course it is possible to have the fallowing scenario :

  • You add a new Guid field + a Property
  • You upgrade the old database schema.. in this case all values will be NULL in the database.
  • If you populate an object having this null column of type Guild of course the Object WILL get an Guid.Empty value HOWEVER if you use an LINQ query ... in the LINQ query it looks the Guid is not yet populated so you need to use == null. Maybe it is a bug but this is the way it is.

In short (using OpenAccess but probably not only) :

var item = GetItems().Where(i => i.SomeGuidField == null); will work and u will get items with null guid this is after an schema update. item.First().SomeGuidField will return Empty Guid

var item = GetItems().Where(i => i.SomeGuidField == Guid.Empty); will not work even if after the population of the item it will be Guid.Empty and will return empty result.

jNayden
  • 1,592
  • 2
  • 17
  • 29
  • 3
    That's not actually `Guid == null` returning true; that's a query engine that interprets expression trees differently than C# does. – SLaks Jun 19 '13 at 14:31
0

Of course this is not only an issue for Guid. The same behavior is seen with any struct type which is not a pre-defined type of C# provided that the struct overloads operator == in the usual way. Other examples in the framework include DateTime and TimeSpan.

This deserves a compile-time warning since, while technically legal because of the lifted operator, this is not a useful comparison since it always gives false. As such, it is an indication of a programmer mistake.

As Eric Lippert said in his answer, the compile-time warning existed with the Visual C# 2.0 compiler. In versions 3.0 through 5.0, the warning was accidentally omitted (for these "user-defined" struct types, but not for pre-defined value types like int, and not for enum types).

Since C# 6.0 (based on Roslyn), the compiler detects this code problem once again. However, because of backwards compatibility(?!), the warning is not issued unless you compile your code with the so-called strict feature.

To enable strict when you use a .csproj file (most usual case), unload the project from Visual Studio, edit the file, insert the XML element:

<Features>strict</Features>

into each <PropertyGroup> (there will usually be more than one) of the .csproj file. You then get the warning (can be "promoted" to an error if you use Treat warnings as errors).

If you cannot edit the .csproj and if you call msbuild.exe from the command line for compiling, use the switch:

/p:Features=strict

to msbuild.exe.

If you do not use .csproj files because you compile directly with csc.exe (the C# compiler), use the switch:

/features:strict

to csc.exe on the command line.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181