Click here to Skip to main content
Click here to Skip to main content

Effective C# - Part I

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

Introduction

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.

Background

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.

Contents

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:

//NO
if ( str != “”)//comparison of two strings {...}
//YES
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:

//NO
String strConcat;
ArrayList arrayOfStrings = new ArrayList();
arrayOfStrings.Add("a"); 
arrayOfStrings.Add("b");
foreach (string s in stringContainer) {
  strConcat += s; 
}

//YES
StringBuilder sbConcat = new StringBuilder ();
foreach (string s in arrayOfStrings ) { 
  sbConcat.append(s);
}

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:

//NO
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)

//YES
//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";
//No:
if(s1 == s2){...}
//Yes:
if(s1.Equals(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:

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

//for
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:

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

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

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

Code snippets:

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

//YES
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;}

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

//Yes:
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.
  o.Lie();
}

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; 
[assembly:CLSCompliant(true)]

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:

//No:
GO.Collect();
//Yes:
GC.Collect(0);

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;
[StructLayout(LayoutKind.Sequential)] 
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.

License

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

Share

About the Author

No Biography provided

Comments and Discussions

 
GeneralMy vote of 1 PinmemberJalalAldeen16-Feb-10 22:41 
GeneralRe: My vote of 1 PinmemberMarcus Kramer3-Dec-10 13:36 
GeneralFOREACH vs FOR, tested again. FOR wins. Pinmemberbluedog says3-Jun-09 11:32 
Generalthank you Pinmemberjustintimberlake24-Feb-09 22:56 
GeneralMy vote of 1 Pinmemberhassan salman25-Jan-09 2:59 
Generalfor vs foreach Pinmemberwitnes11-Jun-08 2:01 
GeneralRe: for vs foreach Pinmemberxusword2-Apr-09 2:18 
GeneralItem 1 string.IsNullOrEmpty() PinmemberMFC User113-Sep-06 22:04 
Generaluse the count property in loops PinmemberCohen Shwartz Oren8-Feb-06 4:32 
Generalif (true == ...) statements Pinmemberdumky13-Dec-05 8:41 
In a number of examples this format is used. Probably doesn't make a difference in execution speed, but it's redundant code.
 
Instead of "if (true == blah) {...", why not simply use "if (blah) {...".
 
For example, "if (true == (o is Person)) {..." would become "if (o is Person) {...".
GeneralRe: if (true == ...) statements PinmemberJun Du8-Mar-06 6:54 
GeneralRe: if (true == ...) statements Pinmemberahz19-Jun-06 8:34 
GeneralItem 4 ... PinsussAnonymous22-Apr-05 11:22 
Generalitems 1 &amp; 4: null checking before string comparison PinmemberJeff Hoover18-Apr-05 4:36 
GeneralItem 3 is just plain wrong! PinmemberexDreamDuck16-Apr-05 8:34 
GeneralRe: Item 3 is just plain wrong! Pinmemberchriswa16-Apr-05 15:01 
GeneralRe: Item 3 is just plain wrong! PinsussAnonymous22-Apr-05 11:15 
QuestionWhy do you test for true? PinsussAnonymous12-Apr-05 20:29 
AnswerRe: Why do you test for true? PinmemberChristian Graus12-Apr-05 20:43 
GeneralRe: Why do you test for true? Pinsussreman12-Apr-05 21:44 
GeneralRe: Why do you test for true? PinmemberFrank Hileman13-Apr-05 3:45 
QuestionFeedback: looking for more of this? PinmemberRoman Antchik12-Apr-05 14:21 
GeneralItem 6 - wrong point of view. PinmemberPeter Ritchie12-Apr-05 9:28 
GeneralRe: Item 6 - wrong point of view. PinmemberSlavaT12-Apr-05 17:59 
GeneralRe: Item 6 - wrong point of view. PinmemberAlan Balkany3-Apr-07 8:50 

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
Web03 | 2.8.141223.1 | Last Updated 5 Apr 2005
Article Copyright 2005 by Cohen Shwartz Oren
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid