Top 10 Fehler–#9: Shying away from exceptions

8. August 2014

Dies ist Teil 9 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Heute zum Thema… Exceptions!

 

Common Mistake #9: Shying away from exceptions

Ich kenne erfahrene Entwickler, die mit Exceptions lieber nichts zu tun haben wollen, weil sie nicht wissen, wie sie damit umgehen sollen. Damit meine ich nicht, dass die Grundlagen von try/catch/throw nicht bekannt wären; vielmehr machen sich viel zu wenig Entwickler Gedanken darüber, wann sie Exceptions werfen, wo sie sie fangen, warum sie das tun, und was dann damit passieren soll.

Ergo ist eine typische Verhaltensweise, Exceptions möglichst schnell plattzumachen oder Code von vorne herein so zu schreiben, das es erst gar nicht zu einer Exception kommt. Was genau das ist, was Patrick in seinem Mistake #9 ankreidet:

Some programmers are so “exception adverse” that they automatically assume the method that doesn’t throw an exception is superior.

Falls Patrick mit seiner Nummerierung eine Gewichtung der Fehler verbindet, dann wäre dieser Punkt bei mir definitiv auf einem der vorderen Plätze.

 

Tatsächlich gibt es sogar wohlgemeinte Ratschläge (der Weg zur Hölle… . Sorry.) in dieser Richtung: Lance Hunt gibt in seinem weit verbreiteten C# Coding Standards Document (alternativer Link) die folgende Empfehlung:

23. Avoid direct casts. Instead, use the “as” operator and check for null.
Example:
object dataObject = LoadData();
DataSet ds = dataObject as DataSet;
if(ds != null)
{…}

Patricks Empfehlung ist da die weitaus bessere:

However, it is incorrect to assume that TryParse is therefore necessarily the “better” method.  Sometimes that’s the case, sometimes it’s not. That’s why there are two ways of doing it. Use the correct one for the context you are in, remembering that exceptions can certainly be your friend as a developer.

Oder um es noch etwas deutlicher zu formulieren:

  • Man verwendet as, TryParse(), FirstOrDefault(), etc., wenn es im Sinne der Programmlogik absolut legal und erwartungskonform ist, dass der Aufruf auch fehlschlagen kann. Das ist immer dann der Fall wenn andere Typen zulässig sind oder wenn zu parsende Daten von außen kommen, etwa aus der Konfiguration, und ähnliches.
  • Man verwendet Casts, Parse(), First(), etc., wenn die Programmlogik ein Fehlschlagen eigentlich nicht zulässt, wenn das also ein Symptom für einen echten Programmierfehler ist. Solche Konstellationen sollten nicht durch irgendeine Fallback-Logik unter den Teppich gekehrt werden, da sie im schlimmsten Fall Folgefehler nach sich ziehen und zu korrupten Daten führen können.

Bezüglich Casts vs. as kommt noch ein weitere Aspekt hinzu: Der Unterschied zwischen den beiden ist tatsächlich größer, als null vs. Exception. Casts führen ggf. Type Conversions durch, während ein as damit nicht viel anfangen kann. Und bei Value Types ist as nicht mal zulässig.

Simples Beispiel:

   1: var i1= 3.5 as int; // Compilerfehler

   2: var i2= (int)3.5; // Typkonversion

Im Grunde gilt wie so oft: “One size fits all” funktioniert nicht. Man kommt nicht umhin den Einzelfall zu betrachten und Pauschalaussagen schaden mehr als sie nutzen.