<filename>(182): Operator '!=' cannot be applied to operands of type
'System.DateTime' and '<null>'
-Bill Mill
bill.mill at gmail.com
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
The translate to if (true), maybe, is it a optimization because the
compiler knows the variable was initialized?
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.
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.
DateTime dt = DateTime.Now;
if (dt>(DateTime.Now-50)) // Significa que la fecha es valida y no
"nula"
inicializacion default.
{
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.
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.
The ECMA specification on page 332: "because [structs] are not
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
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.
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.
I think it's a marginal corner case (literally) due to intersections
of different, *almost* orthogonal concerns.
""""
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...
Object o = null;
SomeMethod(o);
public void SomeMethod(DateTime someDate)
{
DateTime dt = (DateTime)someDate;
if(dt!=null)
{
//...
}
}
That compiles as well.
-TheXenocide
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
> 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)
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.
> 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.
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
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.
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.