Sunteți pe pagina 1din 3

7 Little-Known C# Programming Tricks

1. Indexers can use params

You may be familiar with the regular indexer pattern x = something[“a”]. To implement it you use the following:

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

}

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

Here's an example:

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

}

The interesting thing is that you can have both indexers in the same class side-by-side. If someone passes an array or multiple args they get an IEnumerable back. However, make a call with a single arg and they will get a single value back.

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

instance

It's commonly believed that the following line of code will create a coup of strings every time, however it will not.

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

C#, like many other languages, employs string interning. This means that every string your program compiles with gets put into an in-memory list that is referenced at runtime.

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 runtime has added it to the intern pool.

If you have classes that build up or retrieve regularly used strings at runtime consider using String.Intern to add them to the pool. Bear in mind once in they are in the pool they stay there until the program exists so use String.Intern carefully. The syntax is String.Intern(someClass.ToString())

Another caveat is that performing (object)”Hi” == (object)”Hi” will return true thanks to interning. Try

it in your debug intermediate window and it will be false since the debugger does not intern your

strings.

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

as a real type

A great example of this is when internal lists are exposed as IEnumerable properties as in the

following:

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

You would think you wouldn't be able to modify internal strings, but that's not the case. Here's how:

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

Even AsEnumerable won’t help as that is a LINQ method that does nothing. You can use AsReadOnly which creates a wrapper around the list that throws an exception when you try to set anything. This is a good pattern to use when doing similar things with your own classes if you need to expose a subset of internal structures.

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. Since enums are simply classes it shouldn’t be too surprising that you can extend them like so:

}

}

switch duration {

}

case Day:

case Week: return dateTime.AddDays(7); case Month: return dateTime.AddMonths(1);

default:

return dateTime.AddDays(1);

throw new ArgumentOutOfRangeException("duration")

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 application.

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 0.

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

instances

You might think the following code won'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 that it means only this class can access it, including other instances of this class. This can be quite useful for some comparison methods.