Today I had an interesting chat with one of my colleagues. He sent me this link talking about the new improved C# 6.0 features.
Quite interesting stuff. He had one special thing to discuss about and pointed to these lines code. It is about the null-conditional operator, certainly:
My buddy asked me what do I think about it. Isn’t that great reduction of code size?
Let’s consider that code.
- What is my expectation when calling that function?
- What do I want to accomplish, what do I need to make this code robust?
- How do I make my infrastructure of calling methods and retrieving information unified to all my code?
this is not a comprehensive example. It’s a small piece of code that should show how we can reduce logic and therefore have less lines of code in the first place.
certainly I don’t want to have extra lines of code to check if the return value is null. Why doesn’t this method just simply returns an empty list instead of null?
If this would be the case, there is actual no need to use the null-conditional operator at all. We can happily return the count without any problems of checking. Generelly applied to as a rule we may not even think about return values. (This area is quite some kind of different when we’re talking about algorithims that must be highly optimized).
Actually there is nothing against the usage of the null-conditional operator when it is simple and straight. But what about misuse? Not only shorten the code but thinking about this operator as an ever healing cure.
As described in a former post – What’s wrong with that: I am not that good in naming classes – , I more often see misuses.
Have a look here:
This is a pretty good example of avoiding taking responsibility of the written code. Let’s summarize the usages.
- _excelApplicationManager?: Actually we use dependency injection. This is a service that will be created by Windsor Castle and injected into that class. It will be stored in an readonly field variable. When we really need to do this check, we have a pretty huge problem – then there is something really problematic in our environment. This one is really absolutely senseless and will confuse the reader.
- .Application?: So this may be a valid one? Why not write more readable code by another property called “HasApplication” that this service provides? Especially the code after anyway has to deal with the results. So just returning null value here will lead to not evaluate the full line of code but what then? I prefer to check the necessary variables directly and throw descriptive exceptions that show why the program misbehaves. In this case, there is nothing beside a null exception that will come up somewhen later in code. And then I don’t know what the reason was.
- .ActiveWorkbook?: The same applies here. When the activeworkbook is not specified the program has to communicate that taking action is impossible without that prerequisite.
- .Excel: There is no operator anymore. Why? Cannot be null? Why going back to Excel instance? A lt of question raises here.
- .Sheets(chartGraphic.ShareRange.AlternativeText): I did write about that problems in the other post, but certainly also here we need to act more straight forward. If there is no Sheet with that name it will fail ungracefully.
Again my suggestion is to take responsibility. Every single line of code has a meaning. Shorten code is great when it simplifies, not on cost of readability. Be clear about how to handle null values, either within a method or when returning information after calling a method.
Then this operator will not be overused, and not be used in wrong places.
Though it is true I enjoy your writing style, I don’t agree with your main point of view about this one. I do delight in your website nevertheless. cdfdcaddadef
thanks! What is the point where you don’t agree?
> Why doesn’t this method just simply returns an empty list instead of null?
If it did, the typesystem wouldn’t let you express it, so would you trust that it never would in the future?
Non-null types are IMO the most important missing feature from C# (and many other languages). The reason we have to deal with nulls everywhere is that we can’t opt-out of them. I don’t know why more effort isn’t put into fixing this (especially for new languages – anyone creating a new language with nulls deserves to review null pointer stack traces for all eternity!)
>If it did, the typesystem wouldn’t let you express it, so would you trust that it never would in the future?
That’s a pretty good question, you are completely right. The language doesn’t provide it out of the box. So the developers have to take care about it.
You can ensure these by different strategies. The main question is: Do you want to handle return values in every call to be sure or would it be better to take them out and see this as a responsible part of your automatic testing?
If the team takes responsibility, you can expect developers to keep this rule. But this is certainly more fragile and needs to be supervised.
The problem is, most codebases live for a long time. The main project I work on has code going back over 15 years. Whatever you agree with your team today might not be what’s followed 2, 5, 10 years from now by the new employees after you may have left.
The only thing you can rely on is the type system. I’ve encountered more bugs than I wish to remember in code that was perfectly fine when it was written but some code it called has changed behaviour in a way that still allows it to compile but stops it from working correctly.
(Unsurprisingly) I’m a big fan of strong static typing. I’ve had to maintain enough code written years ago by other people. I trust nothing but the compiler and try to code as defensively as possible. I long for the day that non-nullable types (and contracts, and dependent types!) start showing up in our mainstream languages.
>The problem is, most codebases live for a long time. The main project I work on has code going back over 15 years. Whatever you agree with your team today might not be what’s followed 2, 5, 10 years from now by the new >employees after you may have left.
Completely agree to that.
>The only thing you can rely on is the type system. I’ve encountered more bugs than I wish to remember in code that was perfectly fine when it was written but some code it called has changed behaviour in a way that still >allows it to compile but stops it from working correctly.
With automatic tests and proper behavior/ function testing, it doesn’t matter if it just compiles or not. I am not a friend of 100% coverage, so this is still a problem that can occur, though.
>(Unsurprisingly) I’m a big fan of strong static typing. I’ve had to maintain enough code written years ago by other people. I trust nothing but the compiler and try to code as defensively as possible. I long for the day >that non-nullable types (and contracts, and dependent types!) start showing up in our mainstream languages.
Yes, it should be available. The “null” is some kind of “Variant” back in the old VB6 stuff. The “I-Don’t-need-to-think-about-it-too-much” value. On the other hand, certainly it must be possible to express a state of an object when it wasn’t initialized, which is quite helpful (and memory saving) in a lot of places.
That is actually the challenge: Keep the members of the team on the same page. Keep them responsible and defensive, like you are. Have tests for the critical pieces, and keep going with rules that makes it let’s say avoidable. From my point of view this is often a problem of education – like with all the other issues that are well known and often not considered in software development: Proper and sensible exception handling, logging, good practices in UI development , memory handling, performance to just name a few.
It is an ongoing process, indeed.
> With automatic tests and proper behavior/ function testing, it doesn’t matter if it just compiles or not.
Tests are great; but how do you know what they guarantee? Even if you had 100% coverage; there’s no way to figure out “how well is this actually tested”? Obviously if you’re working on a project that’s mostly just you it’s not so bad, but that’s rarely the case.
My team inherited a complicated codebase once that had THOUSANDS of tests. It was essentially an API, so no UI (really easy to test!). I broke a bunch of stuff and ran the tests. All green. Hmmm. I started poking in the tests and I kid you not, I found MANY instances of this:
and even a few of these:
It made me incredibly sad. The first one is utterly useless and if the second one fails boy do we have problems.
The more we can put onto the type system, the better, IMO. The more errors we can cause to stop compilation, the better. We can ship untested code, we can’t ship uncompilable code. Something I read when I was learning F# was the idea of “make invalid states unrepresentable” and I think the idea makes sense much further than the cases originally described.
> That is actually the challenge: Keep the members of the team on the same page. Keep them responsible and defensive
It is indeed a challenge. Staff change; some are not as motivated as others; sometimes some buckle under pressure or just feel “shipping it when the boss wants it” is more important than “being personally happy with it”. Again; if we can push more stuff to the type system, there isn’t a choice about shipping it early =D
Of course, it’s limited what we can push to the type system (even with contracts, dependent types etc, it’s possible to implement them wrong). But still, going back to the original code, I would avoid taking too much for granted. Assume that the only guarantees about any method you’re calling are the ones the typesystem makes and I think there will be less bugs (there’s the small issue of exceptions violating the contract, but if we assume if an exception occurs we’re already screwed then that’s not a massive problem!)
It’s going a bit offtopic, but I think the general quality of software is really poor lately, even from big companies. I don’t think our languages and tools are helping as much as they could… Check out this blog post and twitter account I set up.. it’s kinda tragic 🙁
I already had a look onto your blog, liked it 🙂
I guess we are talking about the same thing from two different perspectives. You are right, you cannot know if automatic tests are written in a way that they test the right thing instead of just increasing the number of green marks to let the team/ developers feel well.
Let me emphasize one thing: Currently, we cannot push it to the type system, and even if we could, as you stated it wouldn’t solve all the problems. That does mean we have to handle this anyway.
So I believe, this must be done by rules. By education, by taking responsibility, by doing the right thing due to the understanding that that does make the life easier. We do pull requests and reviews to ensure it doesnt happen to often. The sample I added here was exactly the finding of one of these pull requests.
The most attractive solution pushing this to the type system would be nicer, but that works well and the problem can be solved now.
UPDATE: Added a comment to your post, good one 🙂
Comments are closed.