Ten C# Keywords That You Shouldn’t Be Using

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!

Be Sociable, Share!

70 comments

  1. Justin, you have a way with words and explaining things in such a way that it makes perfect absolute unconditional sense to me. You think like me, you talk, well, at least type like me, and you kinda look like me lol.

  2. I don’t believe you should be critical toward the concept of using a goto. I feel that you are overlooking one of the most important keywords in software engineering: “intent.” The goto keyword is absolutely awesome in situations where you know it’s futile to wade through the rest of possibilities in a linear function if the intent is to cut to the chase (output an error, and finish the operation, for example; a good way to discover a practical to do this is to work with berkley sockets).

    Also consider global variables and the Singleton design pattern. Is that design pattern nothing more than a glorified global? Well, it can be argued either way but ultimately it’s determined by the programmer’s intent.

    Then again, I’m making the assumption that your article is intended to be an expression of opinion and I respect your opinion even if I don’t agree with it.

    My intent in leaving this comment is to illustrate that it is all to common for software developers to criticize language constructs before spending time to study the reason(s) why such constructs made it past the cutting room floor (so to speak).

  3. I also disagree with your opposition to the usage of struct. Byte aligned data structures are very friendly to any operating system especially if the data strucure is intended to be used in a container.

    A structure whose size in bytes is a power of 2 is much more quickly indexed (mathematically and in context of element access in contiguous memory i.e. an array) than one that is not. Although this also requires the assumption that the target device for what you are creating isn’t horridly restricted with respect to memory.

    It’s computer architecture 101.

  4. And then what about volatile? Maybe I want a volatile ref char to a part of an I/O port (USB, old school serial, that sort of thing.) What’s wrong with point out, plain as day, that the value contained in this thing might not always be under my control?

  5. The unsafe keyword can be critical when developing systems that interop with unmanaged code. Applying a blanket statement like “you shouldn’t use this” should take into account the small number of cases when you might actually use those keywords. If they were not meant to be used, they wouldn’t exist in the language.

  6. It seems pretty obvious that the audience for this advice is people who don’t know why they are using these things. They all have valid uses, and if you can actually justify them then they are fine and necessary. ref/out with com objects. Structs when you know the use case. Using structs as “lightweight” classes like some people do is totally bonkers. I have seen too much code littered with these where it was absolutely not needed, and the person writing it couldn’t even explain what was really going on.

    This is solid advice for 99% of code. If you can’t understand that, wow.

  7. A good read. But, why limit yourself to the keywords? How about the silly operators.

    ?? – the null coalescing operator.
    Foo y = x ?? new Foo();

    ? – allow primitive variables to be null.
    int? x = null;

    @variables (my favorite idiot operator). For times when you simply must name a variable after a keyword.
    int @float = 5; // create an int named float and set equal to 5.

  8. With the goto keyword you have a point. For the others, I can’t for the life of me think of reasons to avoid them or brand them ‘bad practice’.

  9. Michael Starberg

    I have read the full article and all the comments up to now, and even if I do understand this is general advice for vanilla application code; I still have issues with every single topic. So I’ll add my commentary:

    1) sealed. This topic is not controversial at all. If you are writing leaf application classes then you probably won’t bother with sealing them at all. But if you are writing a public api ment for others, where polymorphism in C# is designed to be non-virtual by default, you will find not allowing sub-classing at all very handy. And I have never heard about the performance argument before. It’s either a virtual call or a direct call. Sure a jitter could figure out if a virtual call could be made into a direct call, but it could discover that without the keyword.

    2) goto. True, goto is in the language. But what we get is the standard tantrum on goto together with a cool Dijkstra quote. What is lacking is how the goto is very limited in C#, not allowing for those pesky longjumps that less protective languages allow for. All this while in the end sporting a perfectly good example of when to use goto, calling it silly.

    3) unsafe. Red herring here. First a laugh at the keyword and then a link to Eric Lippert talking about something completely different. The day you discover you have dimensional data that you want to treat very fast; you’d be glad the unsafe world of pointer arithmetic was included in the language.

    4) ref. Red herring again. The example at the end shows when an instance of a class would be much better. But I see no example of proper use of ref nor how ref differs from out.

    5) fixed. This one goes to show that the author is not using unsafe nor threading very often. goto volatile 9 for reference.

    6) stackalloc. Another red herring. If you are already unsafe, you would most likely want to allocate memory. I see no mentioning of the framework call to Marshal.AllocHGlobal() that would also be highly useful in this context.

    7) unchecked. On the surface this seems like a useless keyword. But I don’t see any reference to the compiler switch /checked. No mention of why it would be a good idea to mark code that will overflow by design (calculating a hashcode for example), with the unchecked keyword.

    8) struct. Red herring yet again. Author sees structs getting abused and conclude the keyword should not be used. How about when you have a complex value type used as key in a dictionary? Would you rather use a class?

    9) volatile. Non-Sequitur. The author says we should use this keyword, but as this is a list of keywords that we should not use, with unsafe and fixed being banned in the first place, my head spins. goto fixed 5.

    10) implicit. A last red herring. We see an example of abuse of subtle implicit casting. But not a word on how implicit/explicit operators should be used. Not using the keyword? You forgot to tell us why.

  10. Have you made a list of interesting things to do with c#?

    There are some interesting things people might want to do with unsafe and pointers; like unmanaged memory streams for example, the garbage collector isn’t always brilliant at knowing when it has an opportunity to drop a large amount of managed garbage.

    BTW your goto example – how would you get out of those loops? Would you perhaps set a flag then break out of the first loop, and then have a second condition in the outer loop to break out of that too if necessary? These sorts of rigmaroles are why there is a school of thought that goto out of nested loops is a legitimate use of goto.

    But its food for thought, I’ve not used struct yet, but I’ll look into it! If it has an advantag ever, I’m going to find it.

  11. sealed is good to avoid fragile baseclass problem.

  12. Ok, it’s a really old post, and maybe the author has a different opinion today but still I thought I answer.

    1.) sealed – Michael has answered the main point so I just add some performance part. If you serialize an array or generic collection of non sealed classes, the type of the elements must be stored for each element. For structs and sealed classes this is not needed.

    2.) goto – Maybe the only point where you are right. Though sometimes it completely makes sense to use goto inside a switch. It does not make the code unreadable at all.

    3.) unsafe – If you can avoid using pointer arithmetic, you are a lucky man. Of course, with IntPtr, Marshal, etc. everything is possible. But if you must not accept the performance of Marshal members, you might need too use unsafe code. In this (hopefully, rare) case the best way is to create a new project, enable unsafe code only for that project and put all of the unsafe routines there.

    4.) ref/out – I would say try to minimize them for public members. But there are typical patterns for them, like bool TryGetSomething(out T result)

    5.) fixed –
    6.) stackalloc –
    goto case unsafe

    7.) unchecked – hex numbers do not support signs. Consider the following example:
    int i = unchecked((int)0xFF000000)
    You cannot avoid using unchecked here, unless you use decimal notation instead of hex, but hex is readable in this case.

    8.) struct – Fixed size immutable types should be structs. So simple. Struct is often misused even in the framework. For example, System.Drawing.Point would be a typically good example for a struct… provided it wasn’t mutable. A Point with another X coordinate is actually another point. Mutable structs are pain in many cases. But if your (fixed-size) type is intended to be immutable, it is the best approach to define it as a struct. And, of course, use IEquatable, implement Equals/GetHashCode correctly and overload == and != operators.

    9.) volatile –
    10.) implicit –
    You say there are some rare cases where they are useful. Everything can be used badly. Why does it mean not to use them at all? Btw, for references use lock instead of volatile. Implicit: do not use between types into a narrowing direction. So simple.

    Conclusion:
    Do not use C#. You can do stupid things with that.

  13. [StructLayout(LayoutKind.Explicit)]
    public struct FloatBits
    {
    [FieldOffset(0)] public uint Bits;
    [FieldOffset(0)] public float Value;

    public FloatBits(float v)
    {
    Bits = 0;
    Value = v;
    }

    public FloatBits(uint bits)
    {
    Value = 0;
    Bits = bits;
    }

    public static implicit operator FloatBits(float value)
    {
    return new FloatBits(value);
    }

    public static implicit operator FloatBits(uint bits)
    {
    return new FloatBits(bits);
    }

    public static explicit operator uint(FloatBits fb)
    {
    return fb.Bits;
    }

    public static explicit operator float(FloatBits fb)
    {
    return fb.Value;
    }

    public unsafe override string ToString()
    {
    char* s = stackalloc char[32 + 1];
    for (var i = 31; i >= 0; i–)
    {
    s[31 – i] = ((Bits >> i) & 1) == 1 ? ‘1’ : ‘0’;
    }
    s[32] = ”;
    var result = new string(s);
    return result;
    }
    }

    public class UnionExample
    {
    public static void TestFloatBits()
    {
    var f = -1f;
    for (var i = 0; i < 10; i++, f += i * 0.1f)
    {
    Console.WriteLine("{0, 12} : {1, 12} : {2, 32}",
    f.ToString("F9"), (uint)(FloatBits) f, (FloatBits) f);
    }
    }
    }

  14. Oops, I hit submit too soon. What I meant to do was demonstrate using “fixed” in the struct as an alternative to “stackalloc”. Also, the ” string termination got reduced to ” when I pasted the code.
    The results of that demo (with bad formatting)…

    -1.000000000 : 3212836864 : 10111111100000000000000000000000
    -0.900000000 : 3211159142 : 10111111011001100110011001100110
    -0.700000000 : 3207803699 : 10111111001100110011001100110011
    -0.400000000 : 3201092812 : 10111110110011001100110011001100
    0.000000030 : 855638016 : 00110011000000000000000000000000
    0.500000100 : 1056964609 : 00111111000000000000000000000001
    1.100000000 : 1066192077 : 00111111100011001100110011001101
    1.800000000 : 1072064103 : 00111111111001100110011001100111
    2.600000000 : 1076258407 : 01000000001001100110011001100111
    3.500000000 : 1080033281 : 01000000011000000000000000000001

    In this demo, I am showing that you can easily “simulate” the useful BitConverter.DoubleToInt64Bits(x) and BitConverter.Int64BitsToDouble(x) methods. And the resulting client code can be very clean and efficient (especially for structs).

    Without these features you might have to do something like…

    BitConverter.ToUInt32(BitConverter.GetBytes(x), 0)

    …and that is, in my opinion, pretty ugly! Not to mention the fact that you would then have to include the code to create a bit string, further muddling more important logic.

    For someone like me who develops quantitative finance systems, the performance gain of a small

    Bottom Line: you can do some fairly useful things with some of the keywords you’ve mentioned. But I agree that you need to thoroughly understand what you are doing!

  15. Nothing is forbidden in programming, unless the people involved don’t have the skills to maintain/understand it. That’s where the real problem lies.

    Programming is engineering and engineering is all about solving problems in the most efficient/suitable way, which by no means should be the most mainstream/safe/clear way there is available.

    That being said, I can pick-point situations where using C# properties or not using a goto is the worst practice (performance critical operations as an example). No one can and should categorize things as bad or good without having a clear overview of the context.

    We live in a world that is not black and white and everything positive has negative aspects included into it, and vice versa.

    And my experience is, that a person that writes crappy code, will continue to do so, even if he avoids using what is considered bad-practices. And the other way around.

    All this hysteria over bad and good practices (including silly stuff like counting the lines of methods, searching the code for goto`s, limiting the max. length of variable and method names, etc) is mostly for people marketing useless books of theirs or project managers that have no idea about programming.

    If a person writes bad code, these are not the things that make it obvious. I have seen code that followed all good practices and its maintainability was below zero (even the person who wrote it couldn’t follow it…).

    Also, if we are to apply the “safeness” rules for JavaScript –for example– we should probably ban the language as a whole (which would be totally OK for me)… However, people are still using this piece of crap, even if using it is like walking on a minefield. And, sure… If you are careful enough and you know what you are doing, you will get what you want working. So, it should not be banned, not even avoided, just I am not sure why browsers still have it as their “one and only” scripting language…

  16. I have yet to find a reason to use these keywords: goto, unsafe, fixed, stackalloc, unchecked, volatile. Not that I wouldn’t use those keywords when/if they are needed, I just have had need yet. So I agree with Justin, de facto.

    I disagree with sealed, ref, and struct. Those are fine things to be used, and ought to be used where appropriate in everyday C# code. Both sealed and struct I commonly use, ref is rarely used (but when it is needed, it’s great). I admit that I often lean towards Tuple return type over ref, but that’s just my druthers and nothing against ref.

    I agree with Justin on implicit (and explicit). I’ve only seen those abused, and never used correctly. I have never had need to use them (which I would only use them correctly). If I had need, I would use them. I liken the implicit and explicit abuse I’ve seen to improper use of IDisposable I’ve seen.

    I like the article. Thought provoking!

  17. Just add “switch” to this list and you’re golden.

  18. This post in a nutshell: “This can be abused, so never use it!”

    Sorry, but that’s not a valid argument at all. Nor do I think the statements are correct. Especially, forbidding sealed and struct are really bad ideas.

    There are more than enough classes that really shouldn’t be inherited; I’d dare say that this is true for the majority of classes. Mark them sealed. If people haven’t heard of composition and encapsulation, they should be off learning, not deriving stuff from other people’s types.

    And prohibiting structs is just silly. It breaks interop. Even where they aren’t required, with current compilers, reference types can be a plain waste of performance. Yea, let’s just make our tiny, immutable encapsulation types… independently allocated objects?! For a 2D-vector maybe this can be argued about, but for something like a wrapped integer it’s just nonsense.

  19. Far too many guidelines expressed as rules here – but then I suppose that’s necessary to attract readers. I have had occasion to use every one of the keywords listed and there are good reasons for all of them to exist. I would agree that they fall into the “advanced” programming category and probably shouldn’t see frequent use, but would contradict your advice and encourage programmers to play with them, understand them and be sure that when they are trotted out, their use is appropriate to the occasion. (Oh, and I too am a business software developer;-)

  20. It sounds like a C# hater who recommend Java XD.

    Like 99% of the C# devs, I often need to store strings in a dictionary, so implicitly, i have to use :
    – A struct keyvaluepair ;
    – Unsafe code for generated the string hash ;
    – Ref for TryGetValue ;

    Occasionaly, I develop a static class, which explicitly means “abstract sealed” (see C# spec).

    What is the problem with “goto”. Even the MSDN documentation agrees with you : “The goto statement is useful to get out of deeply nested loops.”.

Leave a Reply

Your email address will not be published. Required fields are marked *