Click here to Skip to main content
11,485,294 members (75,106 online)
Click here to Skip to main content

Effective C# - Part I

, 5 Apr 2005 185.2K 2 141
Rate this:
Please Sign up or sign in to vote.
Make your C# code efficient.


The following set of effective C# articles contains various ways to improve your C# code.

The Code Project, as a large developers' community, is the right place to discuss ways to write more efficient code. This is a knowledge infrastructure that allows us to become better developers by writing better code. I hope you post new messages and new ways to write effective C# code.


This article is built from separate items. Each item deals with a certain aspect of efficient C# code (Performance, Usage, Garbage collector etc.) followed by code snippets.


Item 1 - Prefer the Length property when checking string size [ Performance]

String comparison involves unnecessary overhead. If all you need is to check whether the string is empty, use the Length property.

Code snippets:

if ( str != “”)//comparison of two strings {...}
if ( str.Length > 0) {...}

Item 2 - Prefer StringBuilder instead of string concatenation. [Performance ]

C# string is immutable, i.e., cannot be altered. When you alter a string, you are actually creating a new string causing the following:

  • The code uses more memory than necessary.
  • Creates more work for the garbage collector.
  • Makes the code execution run slower.

Therefore, you should prefer using StringBuilder (Append method).

Code snippets:

String strConcat;
ArrayList arrayOfStrings = new ArrayList();
foreach (string s in stringContainer) {
  strConcat += s; 

StringBuilder sbConcat = new StringBuilder ();
foreach (string s in arrayOfStrings ) { 

Item 3 - Avoid Boxing and UnBoxing as much as possible. [Performance]

Boxing a value of a value-type consists of two operations:

  • Allocating an object instance.
  • Copying the value-type value into that instance.

Given the above, you should avoid Boxing as much as you can. If you intend to work with ArrayList, for example, do not declare your data type as struct (Value type) because ArrayList works with Object (Reference type) and every time you add an instance of the struct or run over the container, in a loop, a Boxing process will occur.

The following happens when you copy one of the collection items value into the struct:

  • Casting.
  • Copy the value.

Note: Collections expect object type.

Code snippets:

struct st { public Int i; }
Arraylist arr = new ArrayList();
for (int i=0 ; i< count; i++) { 
  st s;
  s.i = 4; 
  arr.item.add(st) ; //<- Boxing (Allocating an object instance
                     // + copying the value-type value into that instance)
st obj = (st ) arr[0]; //<- Unboxing (casting and copy)

//Decalre the data type as class.
Class st { public Int i; }

Item4 - Prefer String.Equal method instead of == operator. [Performance]

Using the string.Equals method is much faster when the strings are matched. So if you are very keen and need special type of performance and expect that most of the strings will be the same, use the Equal method.

Note: The differences in performance are negligible and effects only numerous operations.

Code snippets:

string s1= "code";
string s2 = "code1";
if(s1 == s2){...}

Item5 - Use Native Image Generator (nGen.exe) in case of long and heavy initialization. [Performance]

The .NET Framework runs C# assemblies using the JIT compiler. Each code that is executed for the first time is being compiled. In case of heavy and long initialization in your assembly, you might want to use the nGen .NET tool.

"The nGen creates a native image from a managed assembly and installs it into the native image cache on the local computer.

Once you create a native image for an assembly, the runtime automatically uses that native image each time it runs the assembly. You do not have to perform any additional procedures to cause the runtime to use a native image. Running Ngen.exe on an assembly allows the assembly to load and execute faster, because it restores code and data structures from the native image cache rather than generating them dynamically." (MSDN 2005)

You do not have to lose the advantage of JIT compilation, because you can call the nGen command on the installation machine through your project setup, using:

ngen [options] [assemblyName |assemblyPath ]

Item6 - Prefer 'for' over 'foreach'. [Performance]

'foreach' and 'for' statements serve the same goal - run in loop over block of statements.

The main differences in using the foreach statement are that you do not need to deal with increments and with the end of the loop expression. Moreover, the foreach statement is designed to traverse through the entire collection. One can say that foreach is a private case of for.

In the code snippets below, we can see that both loop blocks produce the same results, only under the hood the foreach hurts the performance. More variables are involved and additional heavy array copy.

The foreach is far more handier to use especially for collections but if your code runs over large collections, prefer using 'for'.

Code snippets:

int[] arrayOfInts= new int[5];
int sum= 0; 
foreach(int i arrayOfInts) {
  sum+= i; 

int[] arrayOfInts= new int[1]; 
int sum= 0;
for(int i = 0; i < arrayOfInts.Length; i++) {
  sum+= arrayOfInts[i]; 

Item7 - Prefer the ‘as’ operator instead of direct type casting. [Usage]

The 'as' operator does not throw an exception. In case of bad cast, the return value is null.

Code snippets:

object o = 1.3; 
  string str = (string)o; 
catch(InvalidCastException ex){...}

string str = o as string;
if(null != str){...}

Item8 - Use the 'checked' keyword to avoid overflow. [Usage]

Code snippets:

short shortNum;
int i = 32768;
shortNum = (short) i; 
// problem after statment excution
// the shortNum variable has an uninitialized value,

try {
  shortNum = checked((short)i); // solution 
catch(OverflowException efx) {}

Item9 - Use the 'is' operator instead of casting. [Usage]

Code snippets:

public class Preson{int nAge;}

static void main(object o){
  try {
    (Person)o.nAge = 45;
  catch(InvalidCastException ex){...}

static void func(object o)
  if ( true == (o is Person) )
    (Person)o.nAge = 45;

Item10 - Use Explicit interface to 'hide' the implementation of an interface [Usage]

Implementing an interface explicitly 'hides' the interface methods. Visual Studio does not display the interface methods in the intellisense.

Code snippets:

//interface definition
Public interface IChild{
  bool IsHuman();
  void lie();

//class definition
Pubic Pinocchio: IChild {
  IChild.IsHuman() //explicit interface implementation
  public void Lie(); //regular interface implementation

//using the object
static void main()
  // Visual studio will not display
  // the isHuman mwthod in the intellisence.
  Pinocchio o = new Pinocchio();
  ((IChild) o).IsHuman(); // using the IsHuman method explicitly.

Item11 - Use @ to ease the work with literal paths. [Usage]

Code snippets:

//Old way
String sFilePath = “c:\\a\\b\\c.txt”;

//The C# way
String sFilePath = @”c:\a\b\c.txt”;

Item12 - Make your API assembly CLS Compliant. [Usage]

The CLS-Compliant attribute cause the compiler to check whether your public exposed types in the assembly are CLS-Compliant.

Prefer to define the attribute for the entire assembly, especially for API. The incentive to create a CLS compliant assembly is that any assembly written in one of the .NET aware languages can use your assembly more efficiently because there is no data types interoperability.

Code snippets:

using System; 

Item13 - Define destructor and implement IDisposable interface for classes that use native resource directly. [Garbage Collection]

You should define a destructor whenever you use native code in your assembly, i.e., use types that are not managed by the garbage collector. The compiler changes the destructor method to Finalize method (can be seen in the MSIL using the ILDasm.exe).

The Garbage collector marks a class with destructor as Finalized and calls the Finalize method while destructing the object. This behavior guarantees that your release of resources in the destructor will occur.

But, what if you want to release the resources immediately? For this purpose, you should implement the IDisposable interface and call the Dispose method when you want to release the object.

Note 1: Do not use destructor if your class does not use native / unmanaged resources. If you do so, you create unnecessary work for the garbage collector.

Note 2: If you implement the IDisposable and a destructor, you should call the Dispose method from the destructor to force the object to release resources immediately.

Code snippets:

~my class {
  if ( true == b) { 

//The MSIL code:
protected override void Finalize() {
  try { 
    if ( true == b) { 
  }finally { base.Finalize();} 

Item14 - Avoid the use of GC.Collect. [Garbage Collection]

The GC.Collect method forces garbage collection of all generations.

The performance is hurt during the call to GC.Collect, the application execution is paused. Moreover, the method might cause the promotion of short lived objects to a higher generation, i.e., those object live longer than it should in the first place.

If you must use it in order to reclaim the maximum amount of available memory, use the method only on generation 0.

Code snippets:


Item15 - Use StructLayout attribute for classes and structs when using COM Interop. [COM Interop]

The attributes cause the compiler to pack the structure in sequential memory so that it can be sent to unmanaged code correctly (unmanaged code that expects a specific layout).

Code snippets:

using System.Runtime.InteropServices;
public struct st{
  int i;
  float f;

What next?

The part II article, will deal with COM Interop in general, and events between managed and unmanaged code in particular.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

No Biography provided

Comments and Discussions

GeneralItem4 Pin
Giancarlo Aguilera6-Apr-05 16:48
memberGiancarlo Aguilera6-Apr-05 16:48 
GeneralRe: Item4 Pin
JanSchreuder18-Apr-05 20:46
memberJanSchreuder18-Apr-05 20:46 
GeneralRe: Item4 Pin
Giancarlo Aguilera19-Apr-05 5:39
memberGiancarlo Aguilera19-Apr-05 5:39 
GeneralWrong Title! Pin
Matt Gerrans6-Apr-05 15:50
memberMatt Gerrans6-Apr-05 15:50 
I'm sure neither Scott Meyers nor Joshua Bloch would be impressed. They both spent a great deal of thought, time and research on their respective works (Effective C++, More Effective C++ and Effective Java) and this list consisting of many premature optimizations (to reiterate Roger's point) at the expense of code reability doesn't follow in that tradition. Several of your items just point out syntax features of C# and don't really have much to do with advice on how best to use the language. With an "Effective C#" article, you should be assuming the reader already knows the features of the language and is looking for how to use it most effectively in the vein of the books mentioned above.

Some specifics on the points:

Item 1:
Did you do any profiling to see how much time you could save with this technique? No? I didn't think so. Don't optimize prematurely at the expense of code readability.

Item 2:
This is good advice for building a big string out of many smaller strings, but not worth the effort, overhead and additional complication when just adding two or three strings.

Item 3:
Again, don't worry about this kind of thing until your profiling shows that you have a problem. Your profiling will probably show your problem is really elsewhere anyway.

Item 4:
WTF | :WTF: No!!! This is ridiculous. Please show your profiler output to prove the value of ever uglying-up your code this way.

Item 5:
This may be a good temporary band-aid when you don't have time to determine what the real causes of your performance problems are.

Item 6:
Show me the profiler output, please!
Try something like this:
private void buttonForSpeed_Click(object sender, EventArgs e)
   const int count = 1000 * 1000 * 100;
   int[] items = new int[count];
   DateTime start = DateTime.Now;
   int sum = 0;
   for( int i = 0; i < items.Length; i++ )
      sum += items[i];
   labelForTime.Text = string.Format("for took {0}",DateTime.Now-start);
   start = DateTime.Now;
   foreach( int i in items )
      sum += i;
   labelForeachTime.Text = string.Format("foreach took {0}",DateTime.Now-start);
Viola. The foreach loop is slightly faster, not slower. Not that it matters. What's going on inside the loop is much more important than the loop itself. This reminds me of all the stupid programmer tricks people do in C/C++ loops (like iterating backwards) to shave that extra microsecond off a 10 minute loop. Have I already mentioned that you might want to do some profiling first?

Item 7:
If you expect a Duck and you don't get one, you probably should be throwing an exception.

Item 8:

D'Oh! | :doh: Items 7 and 8 are in conflict. If you prefer as to casting because it avoids exceptions, then you should prefer something like this to checked:
   if( i <= short.MaxValue )
      shortNum = i;
Item 9:
Why? Is it better to check the type then do the cast? It is actually better to get the exception, since that is indeed an exceptional case. Your function expects to always get a Person, so if it doesn't, there should be an exception. What are you doing in the case where the (badly named) o variable is not a Person is failing silently, which is a much more insideous bug.

And why this bulky syntax for checking true values? if ( true == (o is Person) ) should be simply if(o is Person).

And what the heck is this static void main(object o) thingy? Have you tried compiling any of this?

Item 10:
What's your point? That it is good to hide stuff from IntelliSense? The decision to implement implicitly versus explicitly is a complicated issue. Really, you aren't saying anything other than that it is possible to do it explicitly. Auto-completion will tell you that.

Item 11:
Okay. Hardly material of "Effective C#" calibre.

Item 12:

Item 13:
It is probably better and much less confusing to use the using clause where possible instead of calling Dispose() directly.

Item 14:
Beginners (probably especially those coming from C++) always want to force collection. Rarely is there any value in a program doing this. Just relax and the the garbage collector do its job and it will probably do just fine.

Item 15:
Okay, just pointing out another .Net feature.

Overall, I'd say when you name your article Effective C#, you are setting the bar too high, because both Effective C++ and Effective Java are really classic works of the highest quality. If this article was called "Some C# Tips" or something innocuous like that, I would have been a lot less harsh on it.

Matt Gerrans
GeneralRe: Wrong Title! Pin
Peter Ritchie12-Apr-05 8:28
memberPeter Ritchie12-Apr-05 8:28 
GeneralItem # 1 - Prefer the Length property when checking string size Pin
malharone6-Apr-05 15:17
membermalharone6-Apr-05 15:17 
GeneralRe: Item # 1 - Prefer the Length property when checking string size Pin
malharone6-Apr-05 15:20
membermalharone6-Apr-05 15:20 
GeneralRe: Item # 1 - Prefer the Length property when checking string size Pin
Anonymous6-Apr-05 19:17
sussAnonymous6-Apr-05 19:17 
GeneralRe: Item # 1 - Prefer the Length property when checking string size Pin
RussKie9-Apr-05 13:22
memberRussKie9-Apr-05 13:22 
GeneralRe: Item # 1 - Prefer the Length property when checking string size Pin
Cameron Elliot11-Apr-05 1:40
sussCameron Elliot11-Apr-05 1:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150520.1 | Last Updated 5 Apr 2005
Article Copyright 2005 by Cohen Shwartz Oren
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid