Effective C# - Part I - By Cohen Shwartz Oren

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.

• • • • • • • • • • • • • • • Item1 - Prefer the Length property when checking string size. [Performance] Item2 - Prefer StringBuilder over string concatenation. [Performance] Item3 - Avoid Boxing and UnBoxing as much as possible. [Performance] Item4 - Prefer String.Equal method over == operator. [Performance] Item5 - Use Native Image Generator (Ngen.exe) in case of long and heavy initialization. [Performance] Item6 - Prefer 'for' over 'foreach'. [Performance] Item7 - Prefer the ‘as’ operator over direct type casting. [Usage] Item8 - Use the 'checked' keyword to avoid overflow. [Usage] Item9 - Use the 'is' operator before casting. [Usage] Item10 - Use Explicit interface to 'hide' the implementation of an interface. [Usage] http://www.codeproject.com/script/Submit/ViewHTML.asp?guid=effectivec spart1%2Fcsharp4%2F6%2F2005#item11 Item12 - Make your API assembly CLS compliant. [Usage] Item13 - Define destructor and implement IDisposable interface for classes that use native resources directly. [Garbage Collection] Item14 - Avoid the use of GC.Collect [Garbage Collection] Item15 - Use StructLayout attribute, for classes and structs, when using COM Interop. [COM Interop]

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.