Value Types and Null on 13 Mar 2007

85 views
Skip to first unread message

Bill Mill

unread,
Mar 13, 2007, 5:28:34 PM3/13/07
to tirania.org blog comments.
I can confirm that this throws an error with the C# 1.1 compiler.

<filename>(182): Operator '!=' cannot be applied to operands of type
'System.DateTime' and '<null>'

-Bill Mill
bill.mill at gmail.com

Ben Maurer

unread,
Mar 13, 2007, 5:33:01 PM3/13/07
to tirania.org blog comments.
Mig,

I wonder if this has to do with support for generics, for example, I
think:

bool isNull<T> (T t) { return t == null; }

is a valid function, which returns false if T is a struct or if T is a
normal type and t is null. I guess the most natural way to spec out
this behavior would be that "comparisons between structs and null are
false".

It'd sure be nice if MCS emitted a warning when the statement is
always true/always false.

-b

Jonathan Allen

unread,
Mar 13, 2007, 5:36:30 PM3/13/07
to tirania.org blog comments.
In VB, "If dt = Nothing" would evaluate to True if dt has the default
value for the structure. That would be 0 for numerics and
DateTime.MinValue for dates. Maybe they were toying with the idea of
adding that functionality to C#, then changed their mind.

carles...@gmail.com

unread,
Mar 13, 2007, 5:44:40 PM3/13/07
to tirania.org blog comments.
Well, I've left a copy of .NET book in the job, but I think nullable
type applies to value and reference types. So, it's licit to compare a
value type to null.

The translate to if (true), maybe, is it a optimization because the
compiler knows the variable was initialized?

Nick

unread,
Mar 13, 2007, 5:53:32 PM3/13/07
to tirania.org blog comments.
I actually just found the same problem. I was doing the following:

public float ID
{
get { return _id; }
set
{
if (_id == null)
throw new ArgumentNullException("value");

_id = value;
}
}

The compiler from Microsoft actually does throw a warning "Warning:
The result of the expression is always 'false' since a value of type
'float' is never equal to 'null' of type 'float?'".

Then throws another warning for the "throw new
ArgumentNullException("value");" "Warning: Unreachable code detected"

And according to Reflector the code is just ignored:

public float ID
{
get
{
return this._id;
}
set
{
this._id = value;
}
}

So I think this is just a case of Microsoft ignoring dumb things that
developers do. By the way I wasn't an idiot when I was righting that
code it was generated with a CodeSmith template.

bryan....@gmail.com

unread,
Mar 13, 2007, 5:55:27 PM3/13/07
to tirania.org blog comments.
Miguel, you are too awesome to make the same mistakes Microsoft made
implementing JavaScript. Don't perpetuate what is known to be
incorrect.!

adrian.a...@gmail.com

unread,
Mar 13, 2007, 5:57:38 PM3/13/07
to tirania.org blog comments., ...
Supongo que el problema puede ser que las variables de tipo DateTime
no son inicializadas como nulos, sino con un valor muy bajo, yo lo
haria de la siguiente forma:


DateTime dt = DateTime.Now;

if (dt<(DateTime.Now-50)) // Significa que tiene un valor muy bajo de
inicializacion default.
{
}


No tengo en este momento los medios para probar el codigo pero creo
que debe funcionar.

adrian.a...@gmail.com

unread,
Mar 13, 2007, 6:10:08 PM3/13/07
to tirania.org blog comments.
Correcci[on, debe ser:

DateTime dt = DateTime.Now;


if (dt>(DateTime.Now-50)) // Significa que la fecha es valida y no
"nula"
inicializacion default.
{

Spam...@gmx.de

unread,
Mar 13, 2007, 7:15:23 PM3/13/07
to tirania.org blog comments.
Seems like a bug in csc to me. In fact if you replace the DateTime
structure with e.g. an int it will emit a warning that the expression
is always going to evaluate to false.
And it will emit another warning about unreachable code (it will also
emit this warning with DateTime)
IMHO this would be the best way to go for mono, too. Just hand out a
warning (of course for all cases).

BTW: csc really emits the following code:

int i = 0;
if (0 != 0)
Do something ;)

If you call csc with optimize it will just completely ignore it when
emitting.

juanjo...@gmail.com

unread,
Mar 13, 2007, 7:19:24 PM3/13/07
to tirania.org blog comments.
I think that is a compiler bug maybe because the generics
implementation as Ben Maurer points it out.

The ECMA specification on page 334: "because [structs] are not
reference types, it is not possible for values of a struct type to be
null". So if DateTime is a value type... you can't compare a variable
to values you can't assign.

juanjo...@gmail.com

unread,
Mar 13, 2007, 7:20:11 PM3/13/07
to tirania.org blog comments.
I think that is a compiler bug maybe because the generics
implementation as Ben Maurer points it out.

The ECMA specification on page 332: "because [structs] are not

Martin Plante

unread,
Mar 13, 2007, 7:21:10 PM3/13/07
to tirania.org blog comments.
Hi Miguel.

I believe this is because the non-nullable DateTime could be compared
to another DateTime, but a nullable one. For example, this is totally
logical:

DateTime? nullableDate = null; // Some processing makes this
null
DateTime nonNullableDate = DateTime.Now;

if( nonNullableDate == nullableDate )
...

We can look at this the opposite way:

DateTime? nullableDate = null; // Some processing makes this
null

if( nullableDate < DateTime.Now )
...

Though it doesn't make sense to check if "null" is smaller (or equal,
or any other operator) than a specific DateTime value, it's indeed a
possible instruction.

--
Martin

naas...@gmail.com

unread,
Mar 13, 2007, 7:21:51 PM3/13/07
to tirania.org blog comments.
I just came across this exact situation yesterday in an MSDN article.
Seemed weird to me then too:

http://msdn.microsoft.com/msdnmag/issues/06/00/net/default.aspx

I quote:

Null assignment to a parameterized variable T is disallowed if T is
unbounded, so one might guess that the following would also be
invalid, but it is not:

void Foo<T>(T x) {
if (x == null) { // Ok
···
}
}

If null is allowed here, what happens if T is expanded to a value
type? Where T is a value type, the Boolean expression in the preceding
example is hardwired to false. Unlike in the null assignment case,
null comparison makes sense for any expansion of T.

Spam...@gmx.de

unread,
Mar 13, 2007, 7:26:19 PM3/13/07
to tirania.org blog comments.
Just to clarify the last post:

IMHO it just does an implicit conversion from the native valuetype to
a nullable valuetype. This is probably in line with the specs because
you can do:
DateTime dt = DateTime.Now;
DateTime? dt2 = dt;

So the code probably should be able to compile. But the point that it
is not emitting a warning for DateTime seems to be simply a bug in CSC.

Barry Kelly

unread,
Mar 13, 2007, 7:30:46 PM3/13/07
to tirania.org blog comments.
There is also the related issue of generics. In some type A<T>, values
of type T can be compared with null, which is useful in those cases
where T is a reference - but of course A<T> may be instantiated with a
value type, in which cases the comparisons "ought" to be disallowed.

I think it's a marginal corner case (literally) due to intersections
of different, *almost* orthogonal concerns.

Black Fox

unread,
Mar 13, 2007, 7:51:31 PM3/13/07
to tirania.org blog comments.
They seem to be applying this :

""""
13.3.1 Standard implicit conversions
[...]
* Implicit nullable conversions (§13.7.2)
""""

So as an implicit conversion exists from DateTime to DateTime if we
apply §13.7.2, a standard implicit conversion should happen between
DateTime and DateTime?

As for null, the conversion is not a ""standard"" implicit one, but an
implicit one exists anyway :

""""
13.1 Implicit conversions
[...]
* Conversions from the null type (§11.2.7) to any nullable type
""""

So they seem to translate your sample to :

DateTime dt = DateTime.Now; // this is a value type

if (new DateTime?(dt) != new DateTime?(null)){
}

I never typed code like, and i wouldn't have considered this
logical... But reading the specifications it is normal that it work.

The same reflexion applies with more "normal" types :

class A
{
public static implicit operator int(A test) { return 42; }
}
static void Main(string[] args)
{
A a = new A();
long l = 42;
if (a == l)
{
Console.WriteLine("hello");
}
Console.ReadLine();
}

1. A ""standard"" implicit conversion exists from long to int
2. An implicit (user defined) conversion exists from A to int

is the same as to say :

1. A ""standard"" implicit conversion exists from DateTime to
DateTime?
2. An implicit conversion exists from null to DateTime?

So i think that the microsoft implementation is correct, but the
warning is welcome as it is VERY unexpected by the user...

haa...@gmail.com

unread,
Mar 13, 2007, 8:18:38 PM3/13/07
to tirania.org blog comments.
Well since all value types inherit from System.Object, that might be
the justification. Consider this code:

Object o = null;
SomeMethod(o);

public void SomeMethod(DateTime someDate)
{
DateTime dt = (DateTime)someDate;
if(dt!=null)
{
//...
}
}

That compiles as well.

Black Fox

unread,
Mar 13, 2007, 8:26:17 PM3/13/07
to tirania.org blog comments.
Anyway, tell me if I'm right it interest me.

Ben Monroe

unread,
Mar 13, 2007, 8:34:45 PM3/13/07
to tirania.org blog comments.

asbjornu

unread,
Mar 14, 2007, 3:43:15 AM3/14/07
to tirania.org blog comments.
How about just giving a warning? VS2005 doesn't do it in this
particular case, but in other cases where x and y will never match, it
gives a warning saying "The condition will [never/always] be [true/
false]". Perhaps the Mono compiler could do the same in this case? I
completely agree that it sounds wrong just silently implementing this,
so issuing a warning would be a good compromise, no?

kno...@gmail.com

unread,
Mar 14, 2007, 4:52:42 AM3/14/07
to tirania.org blog comments.
Very curious! Also, note that if you put the inverse condition (==
null), the block under the if is marked with the warning "Unreachable
code" by MS.NET compiler.

TheXenocide

unread,
Mar 14, 2007, 9:08:23 AM3/14/07
to tirania.org blog comments.
Could it have anything to do with the inference of nullable types in
2.0? Not sure necessarily why this would be necessary, but depending
on how much auto-boxing/unboxing happens at the compiler level perhaps
this was a side-effect... heh, just a though :p

-TheXenocide

mtr...@alum.mit.edu

unread,
Mar 14, 2007, 11:41:17 AM3/14/07
to tirania.org blog comments.
I have encountered this in our own code when compiling with Mono.
IMHO, the fact that this compiles with .NET 2.0 is a bug. If there is
a good reason why it should compile, then at the very least I believe
it should result in a compiler warning.

Miguel de Icaza

unread,
Mar 14, 2007, 12:09:31 PM3/14/07
to tiraniaorg-b...@googlegroups.com
Hello,

Well, as I said, we can implement the feature, and we can implement the warning, but the issue is: why does this happen in the first place.  I want to understand the rationale behind this feature and to find some sort of reference as to why this exists in the first place as it does not make a lot of sense to me.

Miguel.
 


Miguel de Icaza

unread,
Mar 14, 2007, 12:13:53 PM3/14/07
to tiraniaorg-b...@googlegroups.com, virtual...@gmail.com
Hello,

    But the problem is that DateTime is not a nullable type, DateTime? (ie, Nullable<DateTime>) would be.

Miguel.

naas...@gmail.com

unread,
Mar 14, 2007, 11:10:30 PM3/14/07
to tirania.org blog comments.
On Mar 14, 12:09 pm, "Miguel de Icaza" <miguel.de.ic...@gmail.com>
wrote:

> Well, as I said, we can implement the feature, and we can implement the
> warning, but the issue is: why does this happen in the first place. I want
> to understand the rationale behind this feature and to find some sort of
> reference as to why this exists in the first place as it does not make a lot
> of sense to me.

As I cited in the msdn article above [1], a value type can never be
null, so the statement is clearly a contradiction and hence always
false. From another perspective, consider the logical runtime
behaviour in equality comparisons:

struct SomeStruct {
//...
public override bool Equals(object o) {
return (o is SomeStruct) && //.. other criteria
}
}

If o is null, as in this case you're considering, then clearly
Equals() always returns false.

Sandro

[1] http://msdn.microsoft.com/msdnmag/issues/06/00/net/default.aspx

Miguel de Icaza

unread,
Mar 15, 2007, 1:29:10 AM3/15/07
to tirania.org blog comments.
Hello,

> As I cited in the msdn article above [1], a value type can never be
> null, so the statement is clearly a contradiction and hence always
> false. From another perspective, consider the logical runtime
> behaviour in equality comparisons:
>
> struct SomeStruct {
> //...
> public override bool Equals(object o) {
> return (o is SomeStruct) && //.. other criteria
> }
>
> }
>
> If o is null, as in this case you're considering, then clearly
> Equals() always returns false.

In the above case if o is a SomeStruct is it boxed, so you would need
to unbox that.

It is still not answering the question why a value type (not a boxed
value type) is allowed by the C# language to be compared with null
when it was a clear violation in C# 1 (and reported in C# 1)


Jeremy Wiebe

unread,
Mar 15, 2007, 9:35:22 AM3/15/07
to tirania.org blog comments.
This is an oldish posting but I think what you're seeing is .NET 2.0's
nullable types.

http://blogs.msdn.com/ericgu/archive/2004/05/27/143221.aspx

Although in that posting he differentiates a nullable from non-
nullable type by placing a question mark after the type.

Eg.

DateTime someDate = null; // Invalid!

DateTime? someDate = null; // Ok.
if (someDate.HasValue) ...

Hope that helps.

andyc...@gmail.com

unread,
Mar 15, 2007, 12:55:30 PM3/15/07
to tirania.org blog comments.
On Mar 15, 5:29 am, "Miguel de Icaza" <miguel.de.ic...@gmail.com>
wrote:

> It is still not answering the question why a value type (not a boxed


> value type) is allowed by the C# language to be compared with null
> when it was a clear violation in C# 1 (and reported in C# 1)

Because a value type can be implicitly converted into a nullable value
type, for the purposes of the comparison. You couldn't do this in C# 1
because C# 1 had no concept of nullable types.

santia...@gmail.com

unread,
Mar 15, 2007, 5:11:47 PM3/15/07
to tirania.org blog comments.
In a similar vein to the diagnosis given by sebmol, I wrote java code
that used autoboxing to handle a Map<String,Integer>. When the key was
absent, a sentence like

String word = "blah";
int i = words.get(word);

was giving NullPointerException, even if there is no null "int" ;) It
was trying to get an Integer.valueOf(null) under the hood, I guess. A
MissingKeyException would have been more appropriate, I guess.

The "magics" of generics and autoboxing biting static typed languages

Miguel de Icaza

unread,
Mar 15, 2007, 6:48:15 PM3/15/07
to tiraniaorg-b...@googlegroups.com
Yes, I agree that in this case it is a valid usage, but my test case happens outside any generic method or generic type.

That is why am not so keen on adding the hack.   We work as expected in the generic case (comparing t of type parameter-of-T to null) but we error out in places where we are not in a generic context.

Miguel de Icaza

unread,
Mar 16, 2007, 1:16:59 PM3/16/07
to tiraniaorg-b...@googlegroups.com
Hello!

    It still does not help;    We do just fine in the presence of Nullable Types, the problem happens in code that has no generic parameters and is used without nullable types.

    I believe the "lifting" might be responsible here as someone posted (a link to a blog), but the blog is just as confused and only offers a guess as to why it is happening.

Miguel.

Guerre...@gmail.com

unread,
Mar 17, 2007, 6:09:31 PM3/17/07
to tirania.org blog comments.
I test it and it's true, but if you tried to compile this:
using System;

class t{
public static void Main(){
DateTime dt = DateTime.Now;
if(dt != null){
Console.WriteLine("swad");
}
Int32 a = 32;
if(a != null){
Console.WriteLine("asdadad2");
}
OtherType u = new OtherType();
if(u != null){
Console.WriteLine("sdad2");
}
}
}

struct OtherType{
int a;
string tmp;
}


the Microsoft C# compiler for CLR V2.0 write one warning and one
error. null.cs(10,6): warning CS0472 and null.cs(14,6): error CS0019,
that's beacuse you can compare a valuetype with null, but with the
example you provided in your post compile so it's strange.

rein...@win.tue.nl

unread,
Mar 20, 2007, 9:50:24 AM3/20/07
to tirania.org blog comments.

I'm not convinced that "cannot be" necessarily means "cannot compare
equal to".
We can also override .Equals for reference types do be something other
than pointer comparison.

mr.mo...@gmail.com

unread,
Mar 22, 2007, 6:44:06 PM3/22/07
to tirania.org blog comments.
Perhaps support the same behaviour as CSC for compatibilities sake,
but issue a warning explaining just what you've said.

Reply all
Reply to author
Forward
0 new messages