In the previous section it was discovered that the ToString() method creates a new string. In this section the inner studies of ToString() are taken one step further in analyzing what implications the ToString() method has on string concatination.
Test Scenario 3:
Let’s modify the test in Test Scenario 1 in a way that this time the output is more decriptive.
int x = 10;
Console.WriteLine("value = " + x);
It is clear that the Console.WriteLine(string) method will be used but will the ToString() be called on variable x? Surprisingly the answer is no. Although it is expected that the variable x is converted to string in reality the compiler will convert the integer to an object using the Boxing technique.
ldstr "value = "
// Pushes a new object reference to a string literal stored in the metadata
ldloc.0
// Loads the local variable at index 0 onto the evaluation stack.
box [mscorlib]System.Int32
// converts a value type to an Object and loads the Object's reference onto the stack
call string [mscorlib]System.String::Concat(object, object)
// Performs a function call to the static method System.Concat(object,object)
call void [mscorlib]System.Console::WriteLine(string)
// Performs a function call to the static method Console.WriteLine(string)
When analyzing the MSIL code generated two big problems can be spotted:
- The integer variable is converted to an object using the box instruction which by is an expensive operation (Refer to Boxing and Unboxing (C# Programming Guide)).
- The Concatination function is being called with object parameters that within the function will still be converted back into strings.
Test Scenario 4:
In test scenario 3 we have identified 2 performance problems, does the ToString() help in removing these issues? Modifying slightly the code, the new MSIL code generated is more optimized.
int x = 10;
Console.WriteLine("value = " + x.ToString());
ldstr "value = "
ldloca.s x
call instance string [mscorlib]System.Int32::ToString()
call string [mscorlib]System.String::Concat(string, string)
call void [mscorlib]System.Console::WriteLine(string)
The MSIL code in Test scenarios 3 and 4 shows that the plus (+) operator on strings is converted to a functional call to String.Concat() . The next 2 scenarios will analyze whether calling the String.Concat() method will make any difference to the previous two test scenarios. Before checking whether calling the String.Concat() will remove the boxing problem in test scenario 3. Let’s verify that calling String.Concat() in combination with the ToString() method will provide the same code as in test scenario 4. It can be confirmed that the plus operator between two strings is a shorthand for using the String.Concat() method. In this test the ToString() method is removed to determine whether the String.Concat() will call the ToString() indirectly since the first parameter is a string. Test scenarios 3 to 6 show that there is no programmatic difference between using the plus (+) operator and the String.Concat() with two values. The choice on the approach is a matter of readability and user preference. Finally, there is another way of joining an integer with a string, which is through parameter passing. Console.WriteLine() is able to take parameters that are referenced through their position. When the code is compiled the boxing concept is still used. The reason is that the integer needs to be passed as an object. Finallay to confirm that using the ToString() will remove the use of the boxing concept let’s call the ToString() on the numeric variable. In a number of cases while developing a program one might require to display the values in a value type instance in a readable format. Value type instances are variables defined using a data type that is defined using the struct construct or a primitive data type. Some examples of value types are int, float, DateTime, etc. When the value type instance is used directly instead of its string representation the CLR will perform a boxing operation to convert the value type into an object, which is used to obtain the string representation of the value type. The box operation is an expensive operation due to the number of operations that are performed on the value type. To help improve the overall performance of your code, .NET provides the ToString() method which is implemented for each value type. The ToString() method will create a new string instance which represents the value type without the need of converting the variable into an object. For more information on the Boxing and Unboxing concepts refer to Boxing and Unboxing (C# Programming Guide) . A number of articles are available on the topic. MSIL descriptions taken from OpCodes Fields and from MSIL StandardTest Scenario 5:
int x = 10;
Console.WriteLine(string.Concat("value = ", x.ToString()));
ldstr "value = "
ldloca.s x
call instance string [mscorlib]System.Int32::ToString()
call string [mscorlib]System.String::Concat(string, string)
call void [mscorlib]System.Console::WriteLine(string)
Test Scenario 6:
int x = 10;
Console.WriteLine(string.Concat("value = ", x));
ldstr "value = "
ldloc.0
box [mscorlib]System.Int32
call string [mscorlib]System.String::Concat(object, object)
call void [mscorlib]System.Console::WriteLine(string)
Test Scenario 7:
int x = 10;
Console.WriteLine("value = {0}", x);
ldstr "value = {0}"
ldloc.0
box [mscorlib]System.Int32
call void [mscorlib]System.Console::WriteLine(string, object)
Test Scenario 8:
int x = 10;
Console.WriteLine("value = {0}", x.ToString());
ldstr "value = {0}"
ldloca.s x
call instance string [mscorlib]System.Int32::ToString()
call void [mscorlib]System.Console::WriteLine(string, object)
Article sections: