Editorial Note: I originally wrote this post for the SubMain blog. You can check out the original here, at their site. While you’re there, have a look at CodeIt.Right to help with automated code review.
A little while back, I started a post series explaining some of the CodeIt.Right rules. I led into the post with a narrative, which I won’t retell. But I will reiterate the two rules that I follow when it comes to static analysis tooling.
- Never implement a suggested fix without knowing what makes it a fix.
- Never ignore a suggested fix without understanding what makes it a fix.
Because I follow these two rules, I find myself researching every fix suggested to me by my tooling. And, since I’ve gone to the trouble of doing so, I’ll save you that same trouble by explaining some of those rules today. Specifically, I’ll examine 3 more CodeIt.Right rules today and explain the rationale behind them.
Mark assemblies CLSCompliant
If you develop in .NET, you’ve no doubt run across this particular warning at some point in your career. Before we get into the details, let’s stop and define the acronyms. “CLS” stands for “Common Language Specification,” so the warning informs you that you need to mark your assemblies “Common Language Specification Compliant” (or non-compliant, if applicable).
Okay, but what does that mean? Well, you can easily forget that many programming languages target the .NET runtime besides your language of choice. CLS compliance indicates that any language targeting the runtime can use your assembly. You can write language specific code, incompatible with other framework languages. CLS compliance means you haven’t.
Want an example? Let’s say that you write C# code and that you decide to get cute. You have a class with a “DoStuff” method, and you want to add a slight variation on it. Because the new method adds improved functionality, you decide to call it “DOSTUFF” in all caps to indicate its awesomeness. No problem, says the C# compiler.
And yet, if you you try to do the same thing in Visual Basic, a case insensitive language, you will encounter a compiler error. You have written C# code that VB code cannot use. Thus you have written non-CLS compliant code. The CodeIt.Right rule exists to inform you that you have not specified your assembly’s compliance or non-compliance.
To fix, go specify. Ideally, go into the project’s AssemblyInfo.cs file and add the following to call it a day.
But you can also specify non-compliance for the assembly to avoid a warning. Of course, you can do better by marking the assembly compliant on the whole and then hunting down and flagging non-compliant methods with the attribute.
Next up, consider a warning to “specify IFormatProvider.” When you encounter this for the first time, it might leave you scratching your head. After all, “IFormatProvider” seems a bit… technician-like. A more newbie-friendly name for this warning might have been, “you have a localization problem.”
For example, consider a situation in which some external supplies a date. Except, they supply the date as a string and you have the task of converting it to a proper DateTime so that you can perform operations on it. No problem, right?
var properDate = DateTime.Parse(inputString);
That should work, provided provincial concerns do not intervene. For those of you in the US, “03/02/1995” corresponds to March 2nd, 1995. Of course, should you live in Iraq, that date string would correspond to February 3rd, 1995. Oops.
Consider a nightmare scenario wherein you write some code with this parsing mechanism. Based in the US and with most of your customers in the US, this works for years. Eventually, though, your sales group starts making inroads elsewhere. Years after the fact, you wind up with a strange bug in code you haven’t touched for years. Yikes.
By specifying a format provider, you can avoid this scenario.
Nested types should not be visible
Unlike the previous rule, this one’s name suffices for description. If you declare a type within another type (say a class within a class), you should not make the nested type visible outside of the outer type. So, the following code triggers the warning.
public class Outer
public class Nested
To understand the issue here, consider the object oriented principle of encapsulation. In short, hiding implementation details from outsiders gives you more freedom to vary those details later, at your discretion. This thinking drives the rote instinct for OOP programmers to declare private fields and expose them via public accessors/mutators/properties.
To some degree, the same reasoning applies here. If you declare a class or struct inside of another one, then presumably only the containing type needs the nested one. In that case, why make it public? On the other hand, if another type does, in fact, need the nested one, why scope it within a parent type and not just the same namespace?
You may have some reason for doing this — something specific to your code and your implementation. But understand that this is weird, and will tend to create awkward, hard-to-discover code. For this reason, your static analysis tool flags your code.
Until Next Time
As I said last time, you can extract a ton of value from understanding code analysis rules. This goes beyond just understanding your tooling and accepted best practice. Specifically, it gets you in the habit of researching and understanding your code and applications at a deep, philosophical level.
In this post alone, we’ve discussed language interoperability, geographic maintenance concerns, and object oriented design. You can, all too easily, dismiss analysis rules as perfectionism. They aren’t; they have very real, very important applications.
Stay tuned for more posts in this series, aimed at helping you understand your tooling.