Compiler issue?
-
Here's an interesting one -- Can someone riddle me this? Given:
using System; enum Foo { Bar, None } class Program { static void PrintArg(object x) { Console.WriteLine(x.ToString()); } static void PrintArg(Foo x) { Console.WriteLine(x.ToString()); } public static void Main() { PrintArg(Foo.None); PrintArg(0); PrintArg(1); } }
Results in the output of:None
Bar
1Note: The application of this issue is apparent in the SqlParameter constructors -- If you do SqlParameter param = new SqlParameter("Name", 0); it calls the SqlDbType override, instead of the object value override. Any ideas why the int 0 isn't acting like the int 1? Is 0 not an int by default? Chadwick
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
-
Here's an interesting one -- Can someone riddle me this? Given:
using System; enum Foo { Bar, None } class Program { static void PrintArg(object x) { Console.WriteLine(x.ToString()); } static void PrintArg(Foo x) { Console.WriteLine(x.ToString()); } public static void Main() { PrintArg(Foo.None); PrintArg(0); PrintArg(1); } }
Results in the output of:None
Bar
1Note: The application of this issue is apparent in the SqlParameter constructors -- If you do SqlParameter param = new SqlParameter("Name", 0); it calls the SqlDbType override, instead of the object value override. Any ideas why the int 0 isn't acting like the int 1? Is 0 not an int by default? Chadwick
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
this is hilarious :p
enum Foo { Bar, None } static void PrintArg(object x) { Console.WriteLine(x.ToString()); } static void PrintArg(Foo x) { Console.WriteLine(x.ToString()); } public static void Main() { PrintArg(Foo.None); PrintArg(Convert.ToInt32(0.ToString())); PrintArg(1); }
your problem is very strange :) here's the output when you cast it to int, Int32, ...
PrintArg(0); //bar
PrintArg((int)0); //bar
PrintArg((Int16)0); //0
PrintArg((Int32)0); //bar
PrintArg((Int64)0); //0-- modified at 14:16 Wednesday 21st March, 2007 -- modified at 14:17 Wednesday 21st March, 2007
Visual Studio can't evaluate this, can you?
public object moo { __get { return moo; } __set { moo = value; } }
-
this is hilarious :p
enum Foo { Bar, None } static void PrintArg(object x) { Console.WriteLine(x.ToString()); } static void PrintArg(Foo x) { Console.WriteLine(x.ToString()); } public static void Main() { PrintArg(Foo.None); PrintArg(Convert.ToInt32(0.ToString())); PrintArg(1); }
your problem is very strange :) here's the output when you cast it to int, Int32, ...
PrintArg(0); //bar
PrintArg((int)0); //bar
PrintArg((Int16)0); //0
PrintArg((Int32)0); //bar
PrintArg((Int64)0); //0-- modified at 14:16 Wednesday 21st March, 2007 -- modified at 14:17 Wednesday 21st March, 2007
Visual Studio can't evaluate this, can you?
public object moo { __get { return moo; } __set { moo = value; } }
So what we're trying to say is that 0 cannot be implicitly boxed by a call to a method if it is in an Int32 valuetype, but it can be boxed if its in an Int16 or an Int64? Any CLR specialists care to take a stab at this one? Anyone try this targeting .NET 1.1? Mine is targeting 2.0... Btw if you call the method as
(object)0
-- it outputs 0 as expected. I also use Resharper, and when I do a (object)1 it tells me the cast is redundant, but when I do a (object)0, it does not!============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
-
So what we're trying to say is that 0 cannot be implicitly boxed by a call to a method if it is in an Int32 valuetype, but it can be boxed if its in an Int16 or an Int64? Any CLR specialists care to take a stab at this one? Anyone try this targeting .NET 1.1? Mine is targeting 2.0... Btw if you call the method as
(object)0
-- it outputs 0 as expected. I also use Resharper, and when I do a (object)1 it tells me the cast is redundant, but when I do a (object)0, it does not!============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
-
So what we're trying to say is that 0 cannot be implicitly boxed by a call to a method if it is in an Int32 valuetype, but it can be boxed if its in an Int16 or an Int64? Any CLR specialists care to take a stab at this one? Anyone try this targeting .NET 1.1? Mine is targeting 2.0... Btw if you call the method as
(object)0
-- it outputs 0 as expected. I also use Resharper, and when I do a (object)1 it tells me the cast is redundant, but when I do a (object)0, it does not!============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
try
PrintArg(-0);
and
Console.WriteLine(0.GetType() == 1.GetType());
and the resharper making it redundant is _very_ odd to say the least. I use it too. It usually looks up whether your type cast has any purpose for this specific case. f.e. casting 0 into object for the purpose of .GetType'ing makes it redundant, because it also has this option without the cast.
Visual Studio can't evaluate this, can you?
public object moo { __get { return moo; } __set { moo = value; } }
-
this is at least a 7 on the WTF-scale.
Visual Studio can't evaluate this, can you?
public object moo { __get { return moo; } __set { moo = value; } }
Code sample:
PrintArg(0); PrintArg((int)0); PrintArg((Int16)0); PrintArg((short)0); PrintArg((Int32)0); PrintArg((long)0); PrintArg((Int64)0); PrintArg(1);
Corresponding IL:.method public hidebysig static void Main() cil managed { .entrypoint // Code size 85 (0x55) .maxstack 1 IL_0000: nop IL_0001: ldc.i4.0 IL_0002: call void Program::PrintArg(valuetype Foo) IL_0007: nop IL_0008: ldc.i4.0 IL_0009: call void Program::PrintArg(valuetype Foo) IL_000e: nop IL_000f: ldc.i4.0 IL_0010: box [mscorlib]System.Int16 IL_0015: call void Program::PrintArg(object) IL_001a: nop IL_001b: ldc.i4.0 IL_001c: box [mscorlib]System.Int16 IL_0021: call void Program::PrintArg(object) IL_0026: nop IL_0027: ldc.i4.0 IL_0028: call void Program::PrintArg(valuetype Foo) IL_002d: nop IL_002e: ldc.i4.0 IL_002f: conv.i8 IL_0030: box [mscorlib]System.Int64 IL_0035: call void Program::PrintArg(object) IL_003a: nop IL_003b: ldc.i4.0 IL_003c: conv.i8 IL_003d: box [mscorlib]System.Int64 IL_0042: call void Program::PrintArg(object) IL_0047: nop IL_0048: ldc.i4.1 IL_0049: box [mscorlib]System.Int32 IL_004e: call void Program::PrintArg(object) IL_0053: nop IL_0054: ret } // end of method Program::Main
Notice the lack of a box operation any time the Int32 0 value is used... consistent with the call to the wrong functionvoid Program::PrintArg(valuetype Foo)
... but notice the correct box op for the call with 1!?!? Quirky... I am almost convinced this is a compiler bug. The only work around I can think of is to do the following:using System; enum Foo { Bar, None } class Program { static void PrintArg(object x) { if (x is Foo) { PrintFooArg((Foo)x); return; } Console.WriteLine(x.ToString()); } static void PrintFooArg(Foo x) { Console.WriteLine(x.ToString()); } public static void Main() { PrintArg(0); PrintArg((int)0); PrintArg((Int16)0); PrintArg((short)0); PrintArg((Int32)0); PrintArg((long)0); PrintArg((Int64)0); PrintArg(1); PrintArg(Foo.Bar); } }
It prints everything as expected....==========
-
try
PrintArg(-0);
and
Console.WriteLine(0.GetType() == 1.GetType());
and the resharper making it redundant is _very_ odd to say the least. I use it too. It usually looks up whether your type cast has any purpose for this specific case. f.e. casting 0 into object for the purpose of .GetType'ing makes it redundant, because it also has this option without the cast.
Visual Studio can't evaluate this, can you?
public object moo { __get { return moo; } __set { moo = value; } }
Submitted to Microsoft as a Compiler Bug -- https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=264570[^]
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
-
Submitted to Microsoft as a Compiler Bug -- https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=264570[^]
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
Now I know we're not crazy :) From Microsoft -- Thanks for your feedback. We have reproduced this bug on , and we are sending this bug to the appropriate group within the Visual Studio Product Team for triage and resolution. Thank you, Visual Studio Product Team. Posted by Microsoft on 3/22/2007 at 12:21 AM
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
-
Now I know we're not crazy :) From Microsoft -- Thanks for your feedback. We have reproduced this bug on , and we are sending this bug to the appropriate group within the Visual Studio Product Team for triage and resolution. Thank you, Visual Studio Product Team. Posted by Microsoft on 3/22/2007 at 12:21 AM
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
Hmmm. Bart De Smet, C# MVP had this to say:
For the C#-compiler, the token 0 is parsed just as a token of course; it depends on the context what the semantic will be. Nevertheless you can often just think of 0 as a int (= System.Int32) constant. For the following discussion, I've used this piece of code: using System; class Program { enum Foo { Bar, None } static void PrintArg(object x) { Console.WriteLine("O {0}", x.ToString()); } static void PrintArg(Foo x) { Console.WriteLine("F {0}", x.ToString()); } public static void Main() { PrintArg(Foo.Bar); PrintArg(Foo.None); PrintArg((Foo)0); PrintArg((Foo)1); PrintArg(0); // Implicit cast to Foo.Bar PrintArg(1); PrintArg((int)0); // Implicit cast to Foo.Bar PrintArg((int)1); PrintArg((Int32)0); // Implicit cast to Foo.Bar PrintArg((Int32)1); PrintArg((Int16)0); PrintArg((Int16)1); PrintArg((Int64)0); PrintArg((Int64)1); PrintArg((object)0); PrintArg((object)1); } } You'll see the following as the output: F Bar F None F Bar F None F Bar O 1 F Bar O 1 F Bar O 1 O 0 O 1 O 0 O 1 O 0 O 1 Let's start with the first pair. Nothing weird in here; just a case of strong typing and a one-on-one match for a method to be called. Essentially, enums are first class citizens when it comes down to object typing. Behind the scenes, an enums is an int however (in the default case). The second pair is the same as above; using an explicit cast from to the enum type. On the method overload resolution side of the story, there's no difference with the case above. On to pair three. In here, something different happens. C# only allows implicit conversion of the decimal-integer-literal 0 to any enum (section 6.1.3 of the C# 2.0 standard); other literals don't have implicit conversions (so you need to do something like in case 2). Therefore, the first invocation will result in a call to the Bar argument method overload, while the second one doesn't. Any type, including value types likes an int, derives from System.Object, so the other overload is called (boxing happens though behind the scenes). Pair four and pair five are the same since int and Int32 are equivalent in C# due to the built-in type mapping. Therefore, (int)0 and (Int32)0 are the same as just 0, resulting in
-
Hmmm. Bart De Smet, C# MVP had this to say:
For the C#-compiler, the token 0 is parsed just as a token of course; it depends on the context what the semantic will be. Nevertheless you can often just think of 0 as a int (= System.Int32) constant. For the following discussion, I've used this piece of code: using System; class Program { enum Foo { Bar, None } static void PrintArg(object x) { Console.WriteLine("O {0}", x.ToString()); } static void PrintArg(Foo x) { Console.WriteLine("F {0}", x.ToString()); } public static void Main() { PrintArg(Foo.Bar); PrintArg(Foo.None); PrintArg((Foo)0); PrintArg((Foo)1); PrintArg(0); // Implicit cast to Foo.Bar PrintArg(1); PrintArg((int)0); // Implicit cast to Foo.Bar PrintArg((int)1); PrintArg((Int32)0); // Implicit cast to Foo.Bar PrintArg((Int32)1); PrintArg((Int16)0); PrintArg((Int16)1); PrintArg((Int64)0); PrintArg((Int64)1); PrintArg((object)0); PrintArg((object)1); } } You'll see the following as the output: F Bar F None F Bar F None F Bar O 1 F Bar O 1 F Bar O 1 O 0 O 1 O 0 O 1 O 0 O 1 Let's start with the first pair. Nothing weird in here; just a case of strong typing and a one-on-one match for a method to be called. Essentially, enums are first class citizens when it comes down to object typing. Behind the scenes, an enums is an int however (in the default case). The second pair is the same as above; using an explicit cast from to the enum type. On the method overload resolution side of the story, there's no difference with the case above. On to pair three. In here, something different happens. C# only allows implicit conversion of the decimal-integer-literal 0 to any enum (section 6.1.3 of the C# 2.0 standard); other literals don't have implicit conversions (so you need to do something like in case 2). Therefore, the first invocation will result in a call to the Bar argument method overload, while the second one doesn't. Any type, including value types likes an int, derives from System.Object, so the other overload is called (boxing happens though behind the scenes). Pair four and pair five are the same since int and Int32 are equivalent in C# due to the built-in type mapping. Therefore, (int)0 and (Int32)0 are the same as just 0, resulting in
Yes, I got the same general response from MS... Section 13.1.3 of the ECMA standard states that the decimal-integer-literal 0 can be implicitly cast to an enum type. It does not, however, provide the same rationale as your MVP friend said. It definitely displays the stated behavior, because if you do int x = 0; then pass x as your parameter, it does the box properly. I kindof see the point in why the standard is the way it is now. I still think that it should at least generate some sort of level 4 warning or something... it is not like all of us have the time (or the brain space) to memorize the ECMA and C# 2.0 standards...especially one as esoteric as this.
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
-
Yes, I got the same general response from MS... Section 13.1.3 of the ECMA standard states that the decimal-integer-literal 0 can be implicitly cast to an enum type. It does not, however, provide the same rationale as your MVP friend said. It definitely displays the stated behavior, because if you do int x = 0; then pass x as your parameter, it does the box properly. I kindof see the point in why the standard is the way it is now. I still think that it should at least generate some sort of level 4 warning or something... it is not like all of us have the time (or the brain space) to memorize the ECMA and C# 2.0 standards...especially one as esoteric as this.
============================= I'm a developer, he's a developer, she's a developer, Wouldn'tcha like to be a developer too?
Chadwick Posey wrote:
esoteric
Lois Griffin: Peter, it's great they picked your theme, but isn't it a little esoteric? Peter Griffin: Esoteric...? \[Cut-scene: inside Peter's head\] Man 2: Could it mean sexy? Man 3: I think it's a science term. Man 4: Fellas, fellas, esoteric means delicious. \[Back to reality\] Peter Griffin: Lois, Who's the Boss? is not a food. Brian Griffin: Swing and a miss.
I'm sorry, big words usually provoke funny movie or TV quotes, rather than their actual meaning. I'm not a retard, just not native-english-speaking ;)
Visual Studio can't evaluate this, can you?
public object moo { __get { return moo; } __set { moo = value; } }