I'm designing a .NET library that will be used by other developers making both web and desktop applications. I'm overriding ToString()
in various classes to provide information for debugging purposes and for inclusion in application log files.
Some of my classes contain numbers and dates.
Consider an object that contains a DateTime
called date
and a double
called value
(and maybe other fields as well)... If I override that object's ToString()
, I might want to do something like:
public override string ToString() {
return "ObjectName[date=" + date + ", value=" + value + "]";
}
But that will include the result from date.ToString()
and value.ToString()
, which will give me strings that are localized according to Thread.CurrentThread.CurrentCulture
.
And that, to me, seems wrong. The strings returned by my ToString()
implementations are meant for debugging and log messages, not for user interfaces. So I think returning localized strings could only confuse matters. If "2,341" appears in log files, a developer would need to know the value of the thread's CurrentCulture
to know whether it meant 2 thousand 341 or 2 point 341. It's even more confusing with dates - a string like xx/xx/xxxx could be dd/mm/yyyy or mm/dd/yyyy. I don't want my ToString()
methods to create that sort of ambiguity.
So my inclination is to make my ToString()
methods culture-insensitive, to ensure that all returned strings are consistent across cultures. For example, internally my ToString()
methods would do like value.ToString(CultureInfo.InvariantCulture)
to format a number.
However, the norm in .NET libraries seems to be to use CurrentCulture
in default no-args ToString()
implementations. Many objects that have a ToString()
method also have a ToString(IFormatProvider)
method as well. It's as if the designers of .NET decided that the default use of ToString()
should be for user-interface display (localized), and that debugging and logs (for which you'd need to call ToString(CultureInfo.InvariantCulture)
) are secondary.
So if I implement my ToString()
methods in a culture-insensitive way, I feel I'd be going against the grain somewhat. But it seems silly to create culture-sensitive strings by default, when culture-sensitivity makes no sense for log files or debugging.
I could use the CurrentCulture
to represent numbers and dates in my default ToString()
implementations, and also provide ToString(FormatProvider)
methods so that people can get a culture-insensitive string for use in log files etc. But that seems dumb as it's just forcing developers to write more code to get the culture-insensitive string that I'm guessing they'll want (whether they've considered it or not).
The bottom line is that a string like ObjectName[value=12.234, date=2011-10-01]
shouldn't ever appear in a user interface, so why would a programmer ever want it to be localized?
I've been reading the advice in Framework Design Guidelines on implementing ToString()
. Some of the advice seems somewhat contradictory. For example:
I consider
ToString
an especially dangerous method to provide for UI-generic types, because it's likely to be implemented with some specific UI in mind, making it useless for other UI needs. To avoid tempting myself in this way, I prefer to make myToString
output as geeky as possible to emphasize that the only "humans" that should ever see the output are "developer humans" (a subspecies all their own).
and
The most important value of
ToString
is that the debugger uses it as the default way of displaying the object.
don't really seem to fit with:
DO string formatting based on the current thread culture when returning culture-dependent information.
and
use the
CultureInfo
instance returned by a thread'sCurrentCulture
property to format any numeric or date
I'm all for following the guidelines, and writing APIs that do what programmers expect. But if ToString()
is for programmers, then it seems silly to localize it. The C# compiler won't let a programmer write a double literal using a system-dependent decimal separator, so surely ToString()
methods written for programmers should behave similarly?
What do you think? When the output from ToString()
is not intended for use in a user-interface, should the numbers and dates within it be localized or not?
Update
I did some tests using the DebuggerDisplay
attribute, and it looks like, by default, it formats numbers in a culture-insensitive way.
[DebuggerDisplay("[value={value}]")]
class DoubleHolder {
private double value;
DoubleHolder(double value) {
this.value = value;
}
}
[TestMethod]
public void TestDebuggerValue() {
DoubleHolder s = new DoubleHolder(12345678.12345);
string doubleString = TestDouble.ToString();
CultureInfo current = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentUICulture = current;
CultureInfo ui = Thread.CurrentThread.CurrentUICulture;
Debugger.Break();
}
Run that test in the debugger and, when it breaks you can see that the double
contained in DoubleHolder
is formatted with a .
as a decimal separator.
Then close Visual Studio, change your windows Regional Options for Standards and Formats to French, say, and run the test again. You'll see that doubleString
has a ,
as the decimal separator, but the the debugger still shows the double
in DoubleHolder
with a .
I would have liked to test this on a proper French version of Windows, with Visual Studio in French. In Visual Studio, if you go to Tools -> Options -> Environment -> International Settings, you can set the Language to "Same as Microsoft Windows". By default on my installation it was set to "English". But to get Visual Studio in French you need to have Windows in French, and my version of Windows seems to be English only. If anyone has a French Windows, or any other locale that uses ,
as a decimal separator, it'd be great if you could just check whether the debugger uses .
or ,
as the decimal separator in formatted doubles.
I'm wondering if Thread.CurrentThread.CurrentUICulture
might make a difference to how the Visual Studio debugger shows things, and I'm not sure that setting it like I do above would be the same as running Visual Studio completely in French.
But from the above it does look like the debugger consistently uses .
as the decimal separator. This implies to me that a culture-independent ToString() method is fine, probably preferable, if it's intended for debugging purposes.