C# 2.0: Generics, default(T) and compare to null weirdness

<Added additional stuff after a discussion on internal CSharp user list>

I was going through the generics spec in C#2.0 and found something really weird.

default(t)

Consider the following code.

 class MyGenClass<T>{    public void Method(ref T t)    {        t =  null ;     }}

This will not compile because because T may be a value type as well and there is no implicit conversion of null to a value-type. So to handle such situation where you want to reset a type in a generic class the default-value expression was instroduced.

 class MyGenClass<T>{    public void Method(T t)    {        t = default(T);     }}

If the type in default-value expression at run-time evaluates to be a reference type then it'll return null, if its a value-type then it will return the value-types default value (essentially new T() which boils down to re-setting all bits to 0).

null comparison weirdness

Peculiarly though, you are allowed to compare t with null.

 class MyGenClass<T>{    public void Method(T t)    {        if  (t == null )             Console.WriteLine("null");        // do some processing*/    }}

I got a bit confused here, as I always thought that you cannot compare value-types to null. Then I tried the following code.

 int i = 5;if (i == null )     Console.WriteLine("null");

and it compiled fine, with a warning that i == null will always be false. But the weird thing is that it compiled and I just cannot figure out why does the compiler allow comparing a value type with null. However, I accepted it and thought I figured out that since value-type can be compared to null it explains why in the generic class I was able to compare t to null. Things got even weirder when I tried to put a value-type constraint on the generic class

 class MyGenClass<T> where T: struct{    public void Method(T t)    {        if (t == null)            Console.WriteLine("null");        // do some processing*/    }}

Compilation failed, with the statement that == operator cannot be applied to T. Since compiler cannot validate that == operator is overloaded on T it fails. But what I cannot figure out is that even in the case where there were no constrains the compiler in no way can validate the same thing, then why did it allow the comparison with null.

Time to send an email to internal CSharp alias to figureout whats going on....


I did finally send the email to CSharp usergroup and after some discussions this is what came out.

The comparison of the form int a = 5; if (a==null) was invalid in VS 2003, but this has changed with nullable types. The int gets converted to a nullable type and the lifted operator bool operator ==(int? x, int? y); is used (see 2.0 spec 24.3.1) .

This answers why null comparison works in value types like int. Why null comparison is allowed in generic classes has a more involved reasoning. First of all there is an explicit rule in the spec which goes as

There are special rules for determining when a reference type equality operator is applicable. For an equality-expression with operands of type A and B, […] if B is the null type, […] A is a type parameter and at runtime, the type parameter is a value type, the result of the comparison is false.

This rule makes the comparison valid. However, I was not convinced about the reasoning behind the rule. Special cases like this break the flow and intuition one develops in a language. Moreover, it the above rule is valid then I'd argue the following code should also be valid

 class MyGenClass<T> where T: struct{    public void Method(T t)    {        if (t == null)            Console.WriteLine("null");        // do some processing*/    }}

Here for the comparison t == null, T should have been auto-converted to its nullable counterpart T? and the comparison should have gone through (evaluated to false). I think the fact that a generic class with no constraint allows an expression and a generic class with additional constraint do not allow the same is un-intuitive.