2

As I recently found out int? and Nullable<int> are not always interchangeable. (Of course this is true for all type that has a Nullable counterpart, not just int.)

What I wonder about: is there a way to construct valid C# code in which you can replace int? with Nullable<int> (some occurences, so not all, but not necessarily only one) and get a still valid C# code, with different meaning?

I don't count examples as solutions in which the int? and Nullable<int> are part of a string literal, so this trivial case is not what i am looking for:

if ("int?".Contains("int?"))
    Console.WriteLine("true");
else
    Console.WriteLine("false");
// prints "true"

if ("int?".Contains("Nullable<int>"))
    Console.WriteLine("true");
else
    Console.WriteLine("false");
// prints "false"

Also here is a more complex version, which still uses strings, but shows you that it is very well possible to write code with only this difference leading to different meanings (unfortunately only one version is valid C#):

CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerResults results = codeProvider.CompileAssemblyFromSource(new CompilerParameters(), "using System; class Foo { static void Main() { int a=1; var test = a is Nullable<byte> & true; } }");
Console.WriteLine(results.Errors.Count > 0 ? "false" : "true");
// prints "true"

CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerResults results = codeProvider.CompileAssemblyFromSource(new CompilerParameters(), "using System; class Foo { static void Main() { int a=1; var test = a is byte? & true; } }");
Console.WriteLine(results.Errors.Count > 0 ? "false" : "true");
// prints "false"

(On why one compiles while the other doesn't I recommend you to check out this question and answer.)

So my question is can you construct code like that? If not then why not?

Community
  • 1
  • 1
qqbenq
  • 10,220
  • 4
  • 40
  • 45
  • Have you checked the compilation error in the `Nullable` case? – SimpleVar Aug 27 '14 at 10:33
  • 4
    I disagree with the premise; they are always interchangeable, but it is your job to ensure the result is valid C#; parenthesis should be your fix here... adding parenthesis in both cases would avoid the problem (`var test = (a is int?) & b;` and `var test2 = (a is Nullable) & b;`) – Marc Gravell Aug 27 '14 at 10:37
  • @YoryeNathan What do you mean? In that case the second example should compile just fine. The error comes from the case with the byte?, and that error is explained in the [linked answer](http://stackoverflow.com/questions/24305290/type-modifer-precedence-vs-logical-and-operator-vs-address-of-operator/24328448#24328448), it is related to the '?' token being recognized as a ternary operator instead of a type modifier. This is the main idea behind this question. using this info it might be possible to create such code... – qqbenq Aug 27 '14 at 10:38
  • Related, regarding the problem with using `?` within the ternary operator: http://stackoverflow.com/questions/24305290/type-modifer-precedence-vs-logical-and-operator-vs-address-of-operator – BartoszKP Aug 27 '14 at 10:39
  • 1
    @MarcGravell Yes, one should never write such code, but I am not trying to avoid that problem, but to *abuse* it. :) You know, to have fun. – qqbenq Aug 27 '14 at 10:41

1 Answers1

1

Well, a trivial example would be anything that declares a local type called Nullable<T>; int will always resolve to global::System.Int32, and the ? suffix on T? will always resolve to global::System.Nullable<T>, but Nullable<T> written manually will resolve using regular rules - which means it might not be the one you expected:

static void Main()
{
    string s = (new Nullable<int>(123)).GetType().Assembly.FullName;
    string t = (new int?(123)).GetType().Assembly.FullName;
}
struct Nullable<T> {public Nullable(T value){} }
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900