Why doesn't System.Exception.ToString call virtual ToString for inner exceptions?

.net, c#, mono

Solution

`Exception.ToString()` is not something that should be presented to users. It is something that should be presented to developers, perhaps in a log file.

The default implementation of ToString() normally does the "right thing". Notice how it does handle nested exceptions, and multiple exceptions (like `AggregateException` or `SqlException`), and even exceptions across a remoting boundary. Try getting an error during the deserialization of XML with the XML serializer, and you'll really appreciate how much information it gives you - usually enough to point directly to the cause of the exception.

If there is something specific that you would like to see displayed, then you should engage Microsoft on this issue. In particular, you're not likely to get them to simply call your overridden ToString. Clearly, the do not want to. However, if there is something your ToString wanted to display that theirs does not, then talk to them and see if there's another way to go about it, or if there's something they can do in a future release.

Problem

This is the actual source for .NET's `System.Exception.ToString`: ``` public override string ToString() { return this.ToString(true, true); } private string ToString(bool needFileLineInfo, bool needMessage) { string str1 = needMessage ? this.Message : (string) null; string str2 = str1 == null || str1.Length <= 0 ? this.GetClassName() : this.GetClassName() + ": " + str1; if (this._innerException != null) str2 = str2 + " ---> " + this._innerException.ToString(needFileLineInfo, needMessage) + Environment.NewLine + " " + Environment.GetRuntimeResourceString("Exception_EndOfInnerExceptionStack"); string stackTrace = this.GetStackTrace(needFileLineInfo); if (stackTrace != null) str2 = str2 + Environment.NewLine + stackTrace; return str2; } ``` Apart from the sheer ugliness, one can notice that for all inner exceptions the private, non-virtual ToString will be called. In other words, if you overload `ToString` in your exception it won't get called if your exception happens to be nested. Oh, hold on, turns out built-in exceptions have same problems, e.g. `System.IO.FileNotFoundException` prints out path of the file - it is not a part of the Message: ``` public override string ToString() { string str = this.GetType().FullName + ": " + this.Message; if (this._fileName != null && this._fileName.Length != 0) str = str + Environment.NewLine + Environment.GetResourceString("IO.FileName_Name", new object[1] { (object) this._fileName }); ... } ``` But if you wrap an instance... this information will be lost, unless you traverse the exceptions tree yourself and detect exceptions' type or call `ToString` yourself and do some mundane parsing. That's an annoying inconvenience, making logging/writing error dialogs either lose information or being bug-prone. Interestingly, Mono gets it right. Is there any hidden wisdom in .NET's version? EDIT: this is not opinion based question. While I find this design choice annoying, I would like to know the benefits of this approach. Knowing them may be beneficial when desiging new solutions.

Original source