Here’s a few unusual things about C# that few C# developers seem to know about.

1. Indexers can use params

We all know the regular indexer pattern x = something[“a”] and to implement it you write:

public string this [ string key ] { get { return internalDictionary [ key ]; } }

But did you know that you can use params to allow x = something[“a”, “b”, “c”, “d”] ?

Simply write your indexer like this:

public IEnumerable < string > this [ params string [] keys ] { get { return keys . Select ( key => internalDictionary [ key ]). AsEnumerable (); } }

The cool thing is you can have both indexers in the same class side-by-side. If somebody passes an array or multiple args they get an IEnumerable back but call with a single arg and they get a single value.

2. Strings defined multiple times in your code are folded into one instance

Many developers believe that:

if ( x == "" || x == "y" ) { }

will create a couple of strings every time. It won’t.

C#, like many languages, has string interning and every string your app compiles with gets put into an in-memory list that is referenced at run-time.

You can use String.Intern to see if it’s currently in this list but bear in mind that doing String.Intern(“what”) == “what” will always return true as you just defined another string in your source. String.IsInterned(“wh” + “at”) == “what” will also return true thanks to compiler optimizations. String.IsInterned(new string(new char[] { ‘w’,’h’,’a’,’t’ }) == new string(new char[] { ‘w’,’h’,’a’,’t’ }) will only return true if you have “what” elsewhere in your program or something else at run-time has added it to the intern pool.

If you have classes that build up or retrieve regularly used strings at run-time consider using String.Intern to add them to the pool. Bear in mind once in they’re there until your app quits so use String.Intern carefully. The syntax is simply String.Intern(someClass.ToString())

Another caveat is that doing (object)”Hi” == (object)”Hi” will return true in your app thanks to interning. Try it in your debug intermediate window and it will be false as the debugger will not be interning your strings.

3. Exposing types as a less capable type doesn’t prevent use as their real type

A great example of this is when internal lists are exposed as IEnumerable properties, e.g.

private readonly List < string > internalStrings = new List < string >(); public IEnumerable < string > AllStrings { get { return internalStrings ; } }

You’d likely think nobody can modify internal strings. Alas, it’s all too easy:

(( List < string >) x . AllStrings ). Add ( "Hello" );

Even AsEnumerable won’t help as that’s a LINQ method that does nothing :( You can use AsReadOnly which creates a wrapper over the list that throws when you try and set anything however and provides a good pattern for doing similar things with your own classes should you need to expose a subset of internal structures if unavoidable.

4. Variables in methods can be scoped with just braces

In Pascal you had to declare all the variables your function would use at the start of the function. Thankfully today the declarations can live next to their assignment and use which prevents accidentally using the variable before you intended to.

What it doesn’t do is stop you using it after you intended. Given that for/if/while/using etc. all allow a nested scope it should come as only mild surprise that you can declare variables within braces without a keyword to achieve the same result:

private void MultipleScopes () { { var a = 1 ; Console . WriteLine ( a ); } { var b = 2 ; Console . WriteLine ( a ); } }

It’s almost useful as now the second copy-and-pasted code block doesn’t compile but a much better solution is to split your method into smaller ones using the extract method refactoring.

5. Enums can have extension methods

Extension methods provide a way to write methods for existing classes in a way other people on your team might actually discover and use. Given that enums are classes like any other it shouldn’t be too surprising that you can extend them, like:

enum Duration { Day , Week , Month }; static class DurationExtensions { public static DateTime From ( this Duration duration , DateTime dateTime ) { switch ( duration ) { case Day : return dateTime . AddDays ( 1 ); case Week : return dateTime . AddDays ( 7 ); case Month : return dateTime . AddMonths ( 1 ); default : throw new ArgumentOutOfRangeException ( "duration" ); } } }

I think enums are evil but at least this lets you centralize some of the switch/if handling and abstract them away a bit until you can do something better. Remember to check the values are in range too.

6. Order of static variable declaration in your source code matters

Some people insist that variables are ordered alphabetically and there are tools around that can reorder for you… however there is one scenario where reordering can break your app.

static class Program { private static int a = 5 ; private static int b = a ; static void Main ( string [] args ) { Console . WriteLine ( b ); } }

This will print the value 5. Reorder the a and b declarations and it will output **.

7. Private instance variables of a class can be accessed by other instances

You might think the following code wouldn’t work:

class KeepSecret { private int someSecret ; public bool Equals ( KeepSecret other ) { return other . someSecret == someSecret ; } }

It’s easy to think of private as meaning only this instance of a class can access them but the reality is it means only this class can access it… including other instances of this class. It’s actually quite useful for some comparison methods.

8. The C# Language specification is already on your computer

Providing you have Visual Studio installed you can find it in your Visual Studio folder in your Program Files folder (x86 if on a 64-bit machine) within the VC#\Specifications folder. VS 2010 comes with the C# 5.0 document in Word format.

It’s full of many more interesting facts such as:

i = 1 is atomic (thread-safe) for an int but not long

is atomic (thread-safe) for an but not You can & and nullable booleans with SQL compatibility

[Conditional("DEBUG")] is an alternative to #if DEBUG

And to those of you that say “I knew all/most of these!” I say “Where are you when I’m recruiting!” Seriously, it’s hard enough trying to find C# devs with a solid understanding of the well-know parts of the language.

[)amien