Nullable trilogy part III: Nullable as type parameter in a generic class

-

Another com­monly asked ques­tion re­lates to the be­hav­ior of Nullable when used as type pa­ra­me­ter to in­stan­ti­ate a generic class. It might be sur­pris­ing that com­par­ing such a pa­ra­me­ter to null gives al­ways false as a re­sult. As it turns out, this is not re­lated to Nullable, but it is a re­sult of how gener­ics are im­ple­mented. There is a ten­dency to think about gener­ics in a man­ner very sim­i­lar to C++ tem­plates, this view is un­for­tu­nately not cor­rect.

Generics have a run­time rep­re­sen­ta­tion and, as such, the com­piler needs to gen­er­ate IL that works what­ever type is used at run­time to in­stan­ti­ate the generic class. This means that the com­piler can­not call user de­fined op­er­a­tors, be­cause at com­pile time it has no knowl­edge about them. The com­piler does­n’t know at that point which type will be used to in­stan­ti­ate the generic class in the fu­ture. It can­not know that it has to gen­er­ate code to call these user-de­fined op­er­a­tors.

In the same vein the com­piler does­n’t know that a generic class will be in­stan­ti­ated with a Nullable pa­ra­me­ter and so it can­not pro­duce IL to lift op­er­a­tors (i.e. the equal­ity op­er­a­tor ==’ ). The re­sult is that when the pro­gram­mer writes code like t==null’, the com­piler gen­er­ates IL to call the standard’ ==’ op­er­a­tor, which in the case of Nullable re­turns false be­cause t has the run­time type of struct.

A sim­i­lar be­hav­ior is ob­serv­able with the fol­low­ing code us­ing strings:

string s1 = Bob John;

string s2 = Bob;

Console.WriteLine(Equals(s1, s2 + John));

sta­tic bool Equals(T a, T b) where T:class {

re­turn a == b;

}

This code would re­turn false, be­cause the ==’ op­er­a­tor for ref­er­ence types gets in­voked.

A case could be made that the com­piler should gen­er­ate IL to check the run­time type of the generic pa­ra­me­ter and call the cor­rect op­er­a­tor for at least some well-known types. This so­lu­tion would­n’t work for user de­fined op­er­a­tors as the com­piler does­n’t know at com­pile time the set of types that could be used to in­stan­ti­ate a generic class. In gen­eral we don’t like to keep this sort of lists of spe­cial things’ in the code­base, More im­por­tantly the so­lu­tion would im­pose a some­how sig­nif­i­cant per­for­mance penalty at each use of the op­er­a­tor. This feel­ing of hacking’ things and the sig­nif­i­cant per­for­mance prob­lem con­vinced us not to im­ple­ment this so­lu­tion.

 

Tags