Let's say that, like many folks, one of your first tasks each morning is dealing with your email. You arrive at the office, grab yourself a coffee, and settle in to complete this ubiquitous modern task.
Naturally, the first thing you do is open your inbox. Then you open each unread email, select all, and copy it. Next, you open a new instance of Visual Studio, create a console project, add a text file to it, and paste the email text into that text file. By the end of doing this, you have somewhere between 2 and 20 instances of Visual Studio running, depending on how many unread emails you have. At this point, you're ready to read the emails, so you alt-tab among the VS instances, closing each one after you've read the email.
This system works well for you, but there are two major drawbacks. First of all, it's a real hassle to have to create a new project and a text file manually, and then open the text file. It'd be awesome if there were a way that you could make Visual Studio do this for you. And secondly, all of those instances of Visual Studio have a tendency to cause your machine to thrash, and they sometimes just crash.
What to do? I'll come back to that.
New C# Features!
For the moment, I'd like to talk about the latest version of C# (6.0) and how it has introduced some exciting new features. I've personally enjoyed using the nameof feature that allows you to eliminate late binding between strings and names of code elements. I've also found property initializers to be handy for eliminating boilerplate around backing fields that I don't want. But there's one particular feature that I haven't had occasion to use: the null conditional operator. It's this feature I'd like to discuss in more detail.
Consider the following code that takes a house and examines it to determine whether or not a prospective intruder would think someone was home.
public class SecurityAuditor
{
public bool WouldAnIntruderThinkSomeoneWasHome(House house)
{
Light mostProminentLight = house.Floors[1].Rooms["Kitchen"].Lights["Overhead"];
return mostProminentLight.IsOn;
}
}
It's a pretty simple concept. It considers a certain light in the house to be the "most prominent" and, if that light is on, it says that an intruder would think someone was home. I doubt I'm going to win a contract from ADT for this, but it's easy enough to reason about.
But it's also not at all defensive. Look at all of the stuff that can go wrong. Objects null, arrays not initialized or sized properly, dictionaries missing keys... yikes. To fix it, we'd need something like this:
public class SecurityAuditor
{
public bool WouldAnIntruderThinkSomeoneWasHome(House house)
{
if (house != null && house.Floors != null && house.Floors.Length > 1 && house.Floors[1] != null
&& house.Floors[1].Rooms != null && house.Floors[1].Rooms.ContainsKey("Kitchen"))
{
Light mostProminentLight = house.Floors[1].Rooms["Kitchen"].Lights["Overhead"];
return mostProminentLight.IsOn;
}
return false;
}
}
Wow, that's ugly. And we're not even done. I just got tired of typing. There's still all the checks around the room not being null and the lights. I also might have missed something. Fortunately, C# 6 lets us do something cool.
public class SecurityAuditor
{
public bool WouldAnIntruderThinkSomeoneWasHome(House house)
{
Light mostProminentLight = house?.Floors?[1]?.Rooms?["Kitchen"]?.Lights?["Overhead"];
return mostProminentLight.IsOn;
}
}
Those question marks you see in there create a null conditional operator ("?." and "?[") that bake the "if(x == null)" check inline and return null if something along the way is null. By inserting these, we cover all of the defensive programming bases with the exception of checking those dictionaries for key presence. But hey, much better, right?
Well, it is and it isn't. To understand why, let's go back to the "Visual Studio as email client" scheme.
Problematic Coupling
What if I told you that I had the cure for what ails you as you check your email? Now, I know it's pretty obvious how to handle this, but I wouldn't be much of a blogger if I didn't explain things. All you need to do is write a Visual Studio plugin that lets you, with a single keyboard shortcut, create an empty console app, create an "email.txt" file in that app, and dump the paste buffer to that text file. And then, to handle the crashing, you just need to buy more memory. Pretty sweet, right?
Yeah, maybe not. Maybe you'd tell me that this is like treating a compound fracture in your leg by shooting Novocaine into it until your entire lower body was numb, and then continuing about your business. And, you'd be right.
The essential problem here is that this method for checking email is stupid. But, it's stupid in a specific way. It introduces problematic and needless coupling. There's no reason to rely on Visual Studio when checking your email, and there's no need to launch enough instances to thrash your machine. Just use Outlook or your browser for your email.
It's actually the same thing with this code sample, as described by a principle known as the Law of Demeter. All I want to know in my method about an intruder is whether the house is prominently lit. I don't need to know how floors, rooms, and lights all relate to each other to know if light is showing from the outside of the house. Why should I have to change my implementation if the homeowners decide they want to rename "kitchen" to "place where Ug get food?"
Here's what that method ought to look like, removing problematic coupling.
public class SecurityAuditor
{
public bool WouldAnIntruderThinkSomeoneWasHome(House house)
{
if(house == null)
throw new ArgumentException(nameof(house));
return house.IsLightVisibleFromDistance(25);
}
}
Notice that I'm not using the spiffy new operator. I don't need to, now that I'm no longer depending on a heavily coupled implementation in which I'm picking through the guts of someone else's object graph.
If you find yourself using this new operator, it's a smell. Note that I'm not saying the operator is bad, and I'm not saying that you shouldn't use it. Like Novocaine, it has its place (e.g. numbing yourself for the pain of dealing with nasty legacy code). But, like Novocaine, if you find yourself relying on it, there's likely a deeper problem.
Want to build your desktop, mobile or web applications with high-performance controls? Download Ultimate Free trial now and see what it can do for you!