
David Bowie (1969 album)
On the wave of the unforgettable song of David Bowie, here is a short post about the “oddities” around the
Char type, in the .Net Framework.
The problem.
Today I bumped against a strange effect.
At first glance I thought to a bug or something like that, since I saw this effect on the Micro Framework. By the way, the little nephew of the ordinary desktop is working fine, because the same problem happens on any .Net application. Thinking better on the “problem”, well…yeah, it’s not a problem!
Yes, it’s not a bug, but can lead easily to subtle side-effects, mistakes, etc. I hate them, because they lead to a long waste of time, and I don’t have, usually.
So, what?
Open your favorite Visual Studio IDE, then create a simple console application. Afterward, copy-and-paste the following code:
class Program
{
static void Main(string[] args)
{
Print(
'H' + "ello!"
);
Console.WriteLine();
Console.Write("Press any key...");
Console.ReadKey();
}
static void Print(string s)
{
Console.WriteLine(s);
}
}
What do you expect to see in the output window as the “Print” function does?
No doubt:
Hello!
Now, add a piece more to the code.
class Program
{
static void Main(string[] args)
{
Print(
'H' + "ello!"
);
Print(
'H' + 'H' + "ello!"
);
Console.WriteLine();
Console.Write("Press any key...");
Console.ReadKey();
}
static void Print(string s)
{
Console.WriteLine(s);
}
}
At this point what should be the resulting output?

Hmm…that’s really strange!
Let’s add another piece.
class Program
{
static void Main(string[] args)
{
Print(
'H' + "ello!"
);
Print(
'H' + 'H' + "ello!"
);
Print(
'H' + ('H' + "ello!")
);
Console.WriteLine();
Console.Write("Press any key...");
Console.ReadKey();
}
static void Print(string s)
{
Console.WriteLine(s);
}
}
The result begins to give us an hint.

Let’s go further…
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Leading...");
Print(
'H' + "ello!"
);
Print(
'H' + 'H' + "ello!"
);
Print(
'H' + ('H' + "ello!")
);
Console.WriteLine();
Console.WriteLine("Trailing...");
Print(
"Hello" + '!'
);
Print(
"Hello" + '!' + '!'
);
Console.WriteLine();
Console.Write("Press any key...");
Console.ReadKey();
}
static void Print(string s)
{
Console.WriteLine(s);
}
}
Aw!…now the “trailing” section seems working fine even without parenthesis…

The reason behind the “oddity”.
The above behavior is due to the implicit cast conversion of a Char to an Int32: this cast conversion is performed even at compile time whenever possible.
Let’s check the IL code of the beginning of the program.
...
IL_000c: ldc.i4.s 72
IL_000e: box [mscorlib]System.Char
IL_0013: ldstr "ello!"
IL_0018: call string [mscorlib]System.String::Concat(object, object)
IL_001d: call void ConsoleApplication1.Program::Print(string)
IL_0022: nop
IL_0023: ldc.i4 144 //<--the compiler calculated the "sum" of the chars
IL_0028: box [mscorlib]System.Int32 //and recognize it as an Int32
IL_002d: ldstr "ello!"
IL_0032: call string [mscorlib]System.String::Concat(object, object)
IL_0037: call void ConsoleApplication1.Program::Print(string)
IL_003c: nop
IL_003d: ldc.i4.s 72
IL_003f: box [mscorlib]System.Char
IL_0044: ldc.i4.s 72
IL_0046: box [mscorlib]System.Char
IL_004b: ldstr "ello!"
IL_0050: call string [mscorlib]System.String::Concat(object, object, object)
IL_0055: call void ConsoleApplication1.Program::Print(string)
...
This designers’ choice to cast convert implicitly also leads to some curious issues:
class Program
{
static void Main(string[] args)
{
//you may "sum" to chars together
Console.WriteLine();
int x = 'A' + 'B';
Console.WriteLine(x); //yields 131
//explicit cast to int
Console.WriteLine();
object o = 'Q';
int q = (int)o; //invalid cast exception
//sum over a string using Linq
var t = "Hello world!";
int u1 = t.Sum(_ => _);
Console.WriteLine(u1); //yields 1117
int u2 = t.Cast<int>().Sum(_ => _); //invalid cast exception
Console.WriteLine();
Console.Write("Press any key...");
Console.ReadKey();
}
static void Print(string s)
{
Console.WriteLine(s);
}
}
UPDATE: I posted the question on StackOverflow, but -at the moment- none gave a decent answer. Even the fully respectable guess of Eric Lippert leave me unsatisfied, believing much more a “kind of gift” for the C/C++ users, other than an useful rule for a safer programming.
In short, I still believe that was a bad decision.
Be careful!
45.486937
12.295311