Programmeren vereist goede kennis van de gebruikte programmeertaal en de frameworks. Fouten in de code leiden tot onvoorspelbaar gedrag van de gebouwde applicatie. Veel fouten zijn te voorkomen, onder andere door het hebben van meer kennis over de werking van de programmeertaal en de gebruikte frameworks.
In dit artikel bestuderen we de vijf meest voorkomende C# programmeerfouten. Hierbij bespreken we ook "Code smells". Dit zijn codes die technisch correct staan maar in de toekomst leiden tot bugs.
1. Tryparse with default
De eerste van de vijf meest voorkomende C# programmeerfouten is het volgende: In deze code wordt een string waarde geconverteerd naar een integer waarde.
Wanneer de waarde niet is om te zetten, dan moet de waarde 100 terug worden gegeven.
Een voorbeeld van dit probleem is wanneer geen inputwaarde is om te zetten naar een integer waarde, zoals "abc". De default waarde van 100 nooit wordt teruggegeven als de conversie van de waarde mislukt.
Volgens de documentatie van de TryParse functie wordt namelijk de default-waarde van een integer teruggegeven. In dit geval is de waarde 0.
Een correcte manier van het converteren met een default is:
2. Floating-point compare
In bovenstaande code vergelijken we twee floats met elkaar. Zo kijken we of ze gelijk zijn aan elkaar. Het probleem met deze code is dat floating-points numeric types relatief onnauwkeurig zijn.
De verwachting van het bovenstaande voorbeeld is dat de output: “1 - 1 are the same” is. In werkelijkheid komt hier de volgende waarde uit: “1 - 1 are NOT the same”.
Een floating-point waarde wijkt mogelijk af. Dit is afhankelijk van de hoeveelheid in een berekening.
Uit het bovenstaande voorbeeld krijg je de volgende output: “1 - 0.99999999999999989”. Deze code geeft de werkelijke (niet afgeronde) waarde weer.
Nu luidt waarschijnlijk de vraag: Waarom floats gebruiken als ze onnauwkeurig zijn? Dit is voor de snelheid van de berekeningen. Als een grote nauwkeurigheid niet nodig is, zal een floating point een stuk sneller zijn in een berekening.
Dit leid tot de volgende implementatie:
Hierin gebruiken we epsilon om de nauwkeurigheid van de vergelijking aan te geven.
Over het verwerken van floats, kunt u hier meer informatie vinden.
3. Unhandled Exogenous Exceptions
Het volgende voorbeeld valt onder de categorie "Code smells". De basis van iedere code is het afhandelen van exceptions van externe afhankelijkheden. Unhandled Exogenous Exceptions betreffen exceptions die te voorkomen zijn. Het afvangen van exceptions maakt de code inefficiënt. Voorkom dit daarom ook zo vaak mogelijk.
In bovenstaand voorbeeld openen we een bestand. Het kan zijn dat dit bestand niet bestaat. De mogelijke exception die hierbij ontstaan is de FileNotFoundException. Dit is makkelijk te voorkomen door eerst te kijken of het bestand bestaat. De exception is hierbij niet een standaard onderdeel van de flow van de code.
Vervang de bovenstaande code door de volgende code:
Hierbij wordt de FileNotFoundException niet een onderdeel van je standaard flow.
Let wel op dat niet alle exceptions van te voren te controleren en te voorkomen zijn. Het is verstandig om deze code in een generieke try catch te plaatsen.
4. Redundant boolean literal
Dit voorbeeld valt ook in de categorie "Code smells".
Hierin wordt een boolean waarde niet direct gebuikt, maar eerst vergeleken met een andere boolean waarde om tot een resultaat te komen:
In dit voorbeeld vergelijkt een boolean waarde zichzelf met een andere boolean waarde. Dat terwijl de eerste waarde al een boolean waarde is.
De vergelijking met een andere boolean waarde, en het resultaat wat weer een boolean waarde is, is daarom overbodig en ongewenst. Een correcte uitwerking van dezelfde code is:
In dit voorbeeld gebruiken we de boolean waarde direct. Het wordt dus niet eerst vergeleken met een andere boolean waarde.
5. Explicitly throwing the exceptions
In dit voorbeeld wordt een exception afgevangen; daarna opnieuw gegooid. Dezelfde exception, die ergens binnen de try optrad, is op een nieuw punt in de code gemaakt. Namelijk in regel 36.
De stacktrace van de exception wordt hierdoor overschreven. De nieuwe locatie is daardoor opgeslagen. De originele stacktrace gaat daarom verloren.
Dit maakt het debuggen van de foutmelding moeilijker. Dat is omdat de oorsprong van de foutmelding is verdwenen, waardoor het niet meer duidelijk is waar de foutmelding is opgetreden.
Het is mogelijk om de originele stacktrace te behouden en de exception af te vangen door de volgende code te gebruiken:
Met deze code wordt de originele stacktrace van de exception bewaard. De exception met de originele stacktrace wordt opnieuw gegooid.
Conclusie
Veel van de bovenstaande meest voorkomende C# programmeerfouten zijn fouten die soms moeilijk te vinden zijn tijdens het testen van een applicatie. Deze fouten zijn meestal pas zichtbaar als je afwijkt van de zogeheten “happy flow” van de applicatie.
De maatregelen die in de code zijn geplaatst, zorgen ervoor dat het oplossen van die problemen juist moeilijker wordt. Terwijl het juist onverwachte omstandigheden op moet vangen,
We hopen dat we u wat meer kennis hebben bijgebracht over de vijf meest voorkomende C# programmeerfouten.
Voor meer informatie en hulp bij development, aarzel niet en neem contact met ons op.
Ferdi van Til - Software Developer