Posted on 4/2/2009 12:15:28 AM by Justin Etheredge
Let me start off by saying that language design is hard, very very hard. Not only is the technical implementation of the language incredibly difficult, but creating a language that will satisfy any significant percentage of its users is an almost impossible task. With some languages you have considerations such as backward compatibility that will hold it back, and in others you simply have such disparate types of users that you are forced to incorporate some less-than-pure functionality into the language.
The C# language is an interesting beast born out of a C-style syntax, but with an aim on extreme type safety. Like most every language on the .NET platform, one of its main goals was to keep the developer from doing stupid things, or at least keep the developer from forgetting to do certain things. C# though falls into the category I mentioned above where the users of the language are extremely diverse, used in everything from military hardware to videogames to my blog. Because of that there are certain features that different types of developers are going to expect. Business users, for example, will expect speed of development. They want things to be simple, unbreakable, and just easy to develop in general. Videogame writers will want speed and control. Sure they don’t want things to be super hard to develop with, but ease of development isn’t their top priority.
Because of the fact that I come from the world of business software (and not game development), there are many features in C# that I think the average developer just shouldn’t be using unless they have a very good reason to do so. Some of these are very obvious, and others maybe not so much. All of which could get you in a bit of trouble if you use them incorrectly.
1) sealed – I went ahead and put what is probably the most controversial keyword in this list first. Sealed. There are two types of people in the C# world, those who love sealed and those who want to seal those other people in a tomb (how witty). I am in the latter camp. In terms of the .NET Framework, I can understand why Microsoft would want to make certain things sealed, but in most application it just makes absolutely no sense. If you start hearing people talking about performance improvements of sealed classes, and you’re not working on the space shuttle’s guidance system, then smack them. Smack them hard.
2) goto – Some of you may be looking at this and wondering if it is even in the C# language, and the answer to that is yes, yes it is. Some of you may be aware that a goto isn’t really as evil as Dijkstra’s paper might have led you to believe, it is the abuse of gotos that is really bad. In fact, we have many equivalent statements which do the same thing as a goto, only in a more structured manner which doesn’t allow abuse. These are “return” (when used in the middle of a method), “break”, and “continue”. Have you ever used one of these? Sure you have. Have you used a goto? Probably not, and if you do, you better have a good freakin’ reason. Especially if you are using it for something as silly as exiting from a nested loop.
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 100; j++)
{
if (someStupidCheck)
goto dufus;
}
}
dufus:
Console.WriteLine("I'm a dufus.");
3) unsafe – Well, if ever there was a keyword that told you not to use it…. Have you ever used unsafe? Probably not. And don’t start. The unsafe keyword is for people who are… well.. unsafe. You don’t want to be whispered about at the water cooler, do you? Basically unsafe means you are going to be using pointers, and we all know what happens when you start using pointers in managed code. And before you go off saying that you thought that all reference types used pointers, go read (and subscribe to) Eric Lippert’s blog. I would have put pointers in this list, but I specifically said “keywords”.
4) ref – You are probably thinking “Whaaaaat? What is wrong with ref? I use it all the time”. And if you are saying that, then well, you need to have your head examined. 9 times out of ten when I see ref being used it is because the developer who used it was too busy writing crappy code to wonder why they needed ref so often. Sure there are times when you need it, but as with almost anything, you better be able to explain to me exactly why you need it. And if you say that your reason is because you need to return five different scalar values from a single method, I am going to call shenanigans and make you write it again. And this time with your brain on.
public static void TooManyResultsYouIdiot(ref int me, ref int no, ref double design, ref byte software)
5) fixed – If you try to use this in your code, then it is likely that you need to be fixed. The fixed keyword is used for pinning. I’m not sure why they called the keyword fixed and the concept pinning, you would think that they would have just stuck with one. How about “pinned”? Too complicated? Right. Anyways, pinning is the process of fixing a value in memory so that the garbage collector won’t move it. You can’t really expect to have a pointer to something if the freakin GC is going to keep shifting it around, can you? But why would the GC keep moving things around? Well, because the GC compacts the heap occasionally. Don’t understand? Well, all you need to know is that it moves crap around and you can’t trust a variable to be in the same place twice. But you don’t have to deal with this unless you are using pointers, which as I already said, you probably shouldn’t be using.
6) stackalloc This is another one of those “unsafe” operations. You know, the ones I said earlier will force you to get checked out by a doctor. Some of you are probably looking at this and wondering what it is, and that is probably a good thing. But if you don’t know what the “stack” is, then that is a bad thing. If that person is you, then you need to do a bit of reading. stackalloc allows you to declare a block of memory on the stack instead of the heap, this allows you to avoid pinning the memory, since it won’t be affected by the garbage collector. Since nothing you are doing relies on this, stay away! If one day you are sitting there and you say to yourself “man I wish I could just allocate this buffer on the stack before I pass it to this method”, then and only then are you allowed to even look up what this keyword does.
7) unchecked – This is a keyword that you really really shouldn’t be using. First because it often doesn’t affect performance all that much, and secondly because it is so misunderstood that it probably doesn’t even do what you think it does. No, it does not turn off any sort of array bounds checking or anything of the like. No, it does not turn off all arithmetic overflow and underflow checking. It only works with integers. Yep, good old Int32. And you know what else, all integer math in C# is unchecked by default. Unless you turn it on via an option in the project, command line option, or using the “checked” keyword. So, the next time that someone wants to use “unchecked” because they need to speed up a loop, bet them a significant amount of money (or lunch) that it won’t do any good. Cause you’ll win.
8) struct – This is another one of those keywords that people are going to gasp and throw their hands into the air exclaiming that I have no idea what I am talking about. Well, I hate to break it to you, but you probably don’t know what you’re talking about. Many people use structs without understanding the very deep implications of using them. Just to name a few… they use ValueType.Equals instead of Object.Equals, if you pass a struct to a method then the struct is copied, but remember that reference types aren’t on the stack, so just their reference is copied. This can produce some odd behavior. Also, it is possible to box a struct and then have a modified version get passed around when you weren’t expecting it. The copy semantics of structs alone should make you pause if you are considering using one. There are a large number of pitfalls when dealing with structs, so unless you know why you are using it, don’t. Besides, heap allocations are so cheap now, right?
9) volatile – This is yet another keyword that falls squarely into the learn before you use it category. I say this because this is not a keyword to be avoided, but simply a keyword that should be used properly. If you have a field which is a reference type (or a few other specific types) and you know that you are going to be accessing it regularly from multiple threads, then you can mark it as volatile. Volatile basically tells the compiler and jitter to not allow certain optimizations which could cause trouble in multi-threaded code. As with anything though, it is never as simple as it looks.
10) implicit – This keyword made the list because it, in my opinion, can cause very subtle bugs. Not to mention the fact that there are very few legitimate uses for this in most software. The implicit keyword comes into play when implementing conversion operators and it signifies an implicit cast between two types. Now you may not be familiar with the terms “implicit” and “explicit” cast, well, implicit means that the cast just happens (like from short to int) and explicit means that you have to tell the compiler to do it (like when going from int to short). You can do some interesting things, like this:
class Program
{
static void Main(string[] args)
{
Class1 variable = new Class2();
}
}
internal class Class1
{
public static implicit operator Class2(Class1 d)
{
return new Class2();
}
}
internal class Class2
{
public static implicit operator Class1(Class2 d)
{
return new Class1();
}
}
Notice how we are assigning an instance of “Class2” into a variable of type “Class1”. And it compiles and works because an implicit cast is going on here. Sure there are very good uses for this, but there about 18 million more abuses for this. Most likely, if one type can be assigned to a variable of the other type, then it should inherit from it. This is not the case in reference types though, which for the most part, is why this behavior exists.
I’m sure that many of you will disagree with my list, and that is good. Please make sure you leave me a comment and let me know why you think the list is wrong, and what I should add or remove in it!