
ToString is virtual. It returns a string representation. We must override ToString on custom types for the method to be effective. For numeric types, there are performance and functional differences with ToString—these involve the NumberFormat argument.
These C# programs demonstrate how to override the ToString method. They test ToString performance.

To start, the ToString method is a virtual method on the object type. Every type inherits from object; thus, you can use an override ToString method to change the implementation used. Then, when the ToString method is called on the instance, the override method is used. Here, we override the ToString method on the custom type Perl.
Virtual Method Object TypeProgram that overrides ToString [C#]
using System;
class Perl
{
int _a;
int _b;
public Perl(int a, int b)
{
_a = a;
_b = b;
}
public override string ToString()
{
return string.Format("[{0}, {1}]", _a, _b);
}
}
class Program
{
static void Main()
{
Perl perl = new Perl(1, 2);
Console.WriteLine(perl);
}
}
Output
[1, 2]When is ToString called? You can see that the ToString method on the Perl instance is never called directly. This is because it is called inside Console.WriteLine. The Perl instance is received as an object, and the virtual method ToString is called internally. The override ToString specified above is then invoked.

Here is what MSDN says: ToString returns a human-readable string that is culture-sensitive. For example, for an instance of the Double class whose value is zero, the implementation of Double.ToString might return "0.00" or "0,00" depending on the current UI culture. Here I compare ToString() with no parameters (no format string) and ToString() with the NumberFormatInfo specified. ToString() can receive a NumberFormatInfo parameter.
Program that uses ToString [C#]
using System;
using System.Globalization; // Important
class Program
{
static void Main()
{
int a = 4000;
int b = 654;
double c = 453.4;
double d = 50000.55555;
string a1 = a.ToString();
string a2 = a.ToString(NumberFormatInfo.InvariantInfo);
Console.WriteLine(a1 + " " + a2);
string b1 = b.ToString();
string b2 = b.ToString(NumberFormatInfo.InvariantInfo);
Console.WriteLine(b1 + " " + b2);
string c1 = c.ToString();
string c2 = c.ToString(NumberFormatInfo.InvariantInfo);
Console.WriteLine(c1 + " " + c2);
string d1 = d.ToString();
string d2 = d.ToString(NumberFormatInfo.InvariantInfo);
Console.WriteLine(d1 + " " + d2);
}
}
Output
4000 4000
654 654
453.4 453.4
50000.55555 50000.55555Identical results. What we see above is that the ToString() without a parameter (a1, b1, c1, d1) all return the same strings as the overloaded methods with a parameter. What the word invariant means is that the format doesn't vary with the social culture of the machine.
Overload Method
In many programs, there is not a lot of culture-sensitive code. I use ToString() in many places simply to convert an integer to a string (5 to "5", for example). What I found when inspecting the intermediate language is that ToString() with no parameters actually gets the culture internally, but this may be non-optimal.
Internally, the ToString method on the int type uses a property called CultureInfo to get the current culture. It then sends the System.IFormatProvider to the ToString() method. So ToString() with no parameters simply gets NumberFormatInfo on its own.
Intermediate LanguageIn my research, I have found that property accesses like get_CurrentCulture() above are slow. So, my solution to this inefficiency is to pass ToString() an already-created NumberFormatInfo. Here's how I changed the code.
Example that shows NumberFormat [C#] // // A. // This code converts the int 900 to a string. // string a = 900.ToString(); // "900" // // B. // This code converts the int 900 to a string. // Has the same exact results. // NumberFormatInfo n = CultureInfo.InvariantCulture.NumberFormat; string b = 900.ToString(n); // "900"
Difference between versions. Version B is different because it eliminates the need for ToString() to access a property internally. This is a significant optimization for this operation, particularly in tight loops. It is faster: somewhere around 10% for invariant cultures. Here we see the timings.
Benchmark results for ToString
100000000 iterations tested.
i.ToString(): 14882 ms
i.ToString(NumberFormatInfo): 13931 ms [faster]
Code used in benchmark [C#]
// Code used in tight loop:
string a = i.ToString();
// Code used outside of loop:
NumberFormatInfo f = CultureInfo.InvariantCulture.NumberFormat;
// Code used in second tight loop:
string a = i.ToString(f);
Let's consider best practices when using the ToString method. When using simple integers or other value types, specify the invariant culture number format. This improves the relevant execution time by 10%. Also, you can implement a cache for string representations returned by ToString, such as with a lookup table structure.
ToString Integer OptimizationSlow version of code [C#] string a = 5.ToString(); Fast version of code [C#] NumberFormatInfo n = CultureInfo.InvariantCulture.NumberFormat; string a = 5.ToString(n);

We provided an example for using an override method for the ToString virtual method on the object type. Further, we looked at some aspects of the ToString method on the int type in the C# language. Inspecting intermediate language is an excellent way to develop best practices and rules for optimization.
String Type