2

I find the C# exceptions very annoying, because they provide so less information. What is the reason for this?

NullReferenceException or KeyNotFoundExceptions are hard to debug and sometimes you don´t get a linenumber in the stacktrace. Why can the exception itself not provide more informations?

For example:

private Dictionary<string, object> Properties = null;

public void Process(string key)
{ 
    var item = this.Properties[key];
    ....
}

When "Properties"is null I get a NullReferenceException:

"System.NullReferenceException: Object reference not set to an instance to an object"

Why I do not get:

"System.NullReferenceException: Object reference 'Properties' not set to an instance to an object"

This would be more usefull and the CLR does know, which reference is null.

The same when I pass a non existent key e.g. Process("dummy"):

"System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary"

Why I do not get:

"System.Collections.Generic.KeyNotFoundException: The key 'dummy' was not present in the dictionary"

The CLR knows, which key was passed and not found.

I try to debug such errors (which illegal keys are passed) in an productive environment and made the code more robust like:

private Dictionary<string, object> Properties = null;

public void Process(string key)
{ 
  if (this.Properties != null)
  {
    if (this.Properties.ContainsKey(key))
    {
        var item = this.Properties[key];
        ...
    }
    else
    {
        throw new KeyNotFoundException(string.Format("The key '{0}' was not found.", key));
    }
  }
  else
  {
    throw new NullReferenceException(string.Format("The object 'Properties' is null."));
  }
}

But why I have to do this, normally the CLR could tell me what was going wrong in detail. I cannot wrap all codepieces like this to get more informations when an exception happens.

JohnnyBravo75
  • 253
  • 1
  • 2
  • 14
  • In the case of `KeyNotFoundException`, knowing the key may not actually be any help, you are making the assumption it is there regardless of what value it is. As for the `NullReferenceException`, I tend to just make my code robust in terms of checking for null items, there are various ways to do this to reduce the amount you have to code up-front. – Adam Houldsworth Sep 17 '13 at 15:49
  • 1
    You should never have these exception in production because they're extremely easy to debug and prevent. All you have to do is check references are not null before accessing properties or calling methods (which you should be doing anyway) and use the `ContainsKey` method on `Dictionary`. – evanmcdonnal Sep 17 '13 at 15:49
  • Hey it is what it is, it is your responsibility as software developer to make sure that you do not run into these exceptions OR handle these exceptions appropriately. The exception message is giving you enough information to work with. Usually when running into these problems you should have a look at the way your code is written as this is rather a problem with the code design than with the actual exception message provided. – Andre Lombaard Sep 17 '13 at 15:55
  • "You should never have these exception in production because they're extremely easy to debug and prevent". Why does Visual Studio 2013 Preview throws NullReferenceExceptions? ... Because everbody and their code is perfect? The code was a sample an the KeyNotFoundException occured in one place but I cannot be sure that is occurs somewhere else except wrapping the dictionary. My qustion was why does the CLR do not provide more information in an exceptional situation not how to prevent exceptions – JohnnyBravo75 Sep 17 '13 at 16:00
  • 2
    `I find the C# exceptions very annoying, because they provide so less information` have you ever tried compiling a program in C? The errors the compiler (especially GCC) spit out are like `0E0001 ajhfkjashrialufhajkae`. – Arian Motamedi Sep 17 '13 at 16:26
  • Yep I did and I programed assembler (I did 20 years ago) the execeptions where the complete crash or your program. But this times I do nt wish back. c# is managed code and not for performance, but for security and robustness and easyness, or? – JohnnyBravo75 Sep 17 '13 at 16:34

5 Answers5

4

For your KeyNotFoundExceptions, you can create your own dictionary class that throws more meaningful messages (I recommend you try to extend KeyNotFoundException and throw that), or you can use TryGetValue and throw a meaningful exception.

The NullReferenceException one, however, is much more complicated: you assume that the CLR knows that the thing is called Properties, but it's not so simple: consider this.GetSomething(abc).DoSomething(), where GetSomething(abc) returns null. What is the object that's null? It doesn't have a name. And sometimes, especially in Release-optimized code, names for variables or other things that might be null are generated.

You should be debugging with test cases, Asserts, breakpoints, and other debug-mode code, not expecting that you can always get a good enough exception message to debug production code. E.g. even if you know that the key "dummy" was passed in, you might not have enough information to know why that key was bad, or was passed in.

Tim S.
  • 55,448
  • 7
  • 96
  • 122
  • 1
    I know, I found already the NiceDictionary here but this is only a bad workaround for my problem. This does NOT answer my question, why I have to do this, although the CLR has the informations and could pack them into the exception. The first object in the chain which is null e.g. "GetSomething() returned null". – JohnnyBravo75 Sep 17 '13 at 16:25
1

Question: Why do exceptions provide less information than available?

Answer: Possible performance issues.

To produce a nice KeyNotFoundException, you need to call ToString(). This can take considerable time. For example, if I create Dictionary<StringBuilder, int>, failing to find a key would cause a (possibly huge) new memory block to be allocated and filled with data (in .NET 4.5 implementation). Furthermore, this can cause exception message to contain a few megabytes of text.

Question: Why not make exception messages more detailed in Debug mode?

Answer: Inconsistency is bad.

It is extremely difficult to fix bugs which can only be reproduced in Release mode. Getting string representation of keys only in Debug mode will cause diferent behavior:

  1. ToString can throw exceptions.

  2. Lazy programmers can preform checks on exception messages.

Question: But there are suggestions on UserVoice to improve the messages.

Answer: If you like them, vote for them.

I guess that meaningful NullReferenceException in a.b.c.d.e.f.g.h() chain is not top priority. It is annoying, but it has an easy workaround. If you think it is one of the most important problems, then go vote for it. But you may look around and find more interesting stuff to vote for.

Athari
  • 33,702
  • 16
  • 105
  • 146
1

In general, code which throws an exception doesn't know what's going to be done with the message. Conceptually, it might have been possible for an exception to include both Message and a DeveloperPrivateMessage properties, and specify that exceptions should refrain from placing in Message any information which could compromise security if revealed to the general public, and code receiving exceptions should refrain from persisting DeveloperPrivateMessage in any fashion which would expose it to untrusted personnel, but that would have complicated things. Instead, code that throws exceptions is expected to simply refrain from including any confidential data. Since dictionaries have no way of knowing whether keys might be used to store confidential data, that implies that keys should not be included in exception messages.

As for NullReferenceException, that's generally caught by a trap handler which may be able to determine that code was trying to execute a mov eax,[esi+8] when the ESI register was zero, but the trap handler would have no way of knowing where the value in ESI came from. If one says meh = Foo.Bar.Boom, and the Bar property returns null only after modifying Foo so that the next call won't return null, then by the time the system tries to access null.Boom, Foo.Bar won't be null. The best the system could do would be to say that "member Bar of some object was null", but that may not be very helpful.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • Why not provide confidentail data? To prevent hacking? But it also provides (sometimes) the codelines, which is also confidential. For me, getting context information in the exception would be very usefull, and I wish I could switch it on (as an compiler option on my own risk, like "full Debug" option). The CIL canbe fully decompiled, so all context informations and variables names are present and can be extracted and the exception message could contain it. Nevertheless, your answer seems to be logical and precise and I marked it as answer. Thanks! – JohnnyBravo75 Sep 21 '13 at 01:00
  • 1
    @JohnnyBravo75: The issue isn't so much revealing the structure of the code, but revealing information which might in and of itself be confidential. A programmer has no general way of knowing what things like `ToString` might reveal. As a somewhat extreme example, imagine that someone's attempt to register as a new user on a web site at just the right time to cause a dictionary collision, yielding exception "Attempt to add record with key '12345' when such record already exists (key='12345' data='Patient_name="John Smith", Patient_DOB=10/01/1980, Patient_Blood_Type=ABPos, etc.')". – supercat Sep 23 '13 at 15:18
  • 1
    @JohnnyBravo75: It's not hard to imagine a message of the latter form being useful while debugging, but it's also not hard to imagine it opening one up to significant liability in production. Who, though, should be blamed for it? The author of `PatientData.ToString()`, the author of the `Dictionary` exception-generating code, or the author of the code which formatted the message for display? Even though all three pieces of code might be reasonable in and of themselves, it's often best for all three to be written so as to avoid disaster even if the other two behave in "evil" fashion. – supercat Sep 23 '13 at 15:22
  • @JohnnyBravo75: As noted, it might have been nice for `Exception` to provide a standard convention for including both sanitized and non-sanitized information, but I doubt it would make any reasonable top-ten list of features Microsoft should spend resources implementing. – supercat Sep 23 '13 at 15:28
0

Then you should add special handling to get this information. Remember everything comes at some cost. The more bloated you make exception handling (that shouldn't occur in the first place) - the larger the framework would have to be for every single case like this.

You really should consider these "Last chance exceptions" since you should never see them if coded correctly. Between the exception message and the stack trace that is provided - that should be enough information for your.

tsells
  • 2,751
  • 1
  • 18
  • 20
  • If you don't like what is provided - you can always submit an enhancement request to Microsoft. :) – tsells Sep 17 '13 at 16:32
  • I´m not alone, this is already in MS Connect (not from me): http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3539403-improve-exception-messages – JohnnyBravo75 Sep 17 '13 at 16:41
  • Or here ... http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2371587-better-nullpointerexception-error-message – JohnnyBravo75 Sep 17 '13 at 16:42
  • @JohnnyBravo75 Well then there you go. Go complain to the people who are in a position to change the behavior, rather than random strangers not responsible for affecting any change. – Servy Sep 17 '13 at 16:43
  • 1
    I´m not complaining at you I want to know if there is a reason. One person here told me just for performance and that is a explanation. – JohnnyBravo75 Sep 17 '13 at 16:51
0

The responsibility of checking for null or if the key is missing is on you, the developer. The reason for this is optimization; if the default exceptions were more detailed like you would like, then the compiler would not be able to optimize as efficiently.

It's a trade-off. Adding null checks can be annoying, but it's how the language is designed.

User
  • 1,118
  • 7
  • 23
  • Thanks, for the answer. But then they could something like do in "Debug" and strip it in "Release" compile option. I found in meanwhile, that it is already in MS Connect: [link]http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2371587-better-nullpointerexception-error-message[link] and I´m not alone... – JohnnyBravo75 Sep 17 '13 at 16:49