.NET Best Practices

September 29, 2008
Contents

Managed Heap and Garbage Collection Overview
Memory Allocation
De-allocation of Memory
Object Finalization
Generations in GC
Minimize Boxing and Unboxing
Finalize() Method Implementation
Implement the Standard Dispose Pattern
Utilize using and TRy/finally for Resource Cleanup
Always Use Properties Instead of Accessible Data Members

Prefer readonly to const
Prefer the is or as Operators to Casts
Always Provide ToString()
Ensure that 0 is a valid State for Value Types
Consider overriding the Equals() method for value types
Understand the Relationships Among ReferenceEquals(), static
Equals(), instance Equals(), and operator==

Avoid pre-allocating memory
Prefer Variable Initializers to Assignment Statements
Initialize Static Class Members with Static Constructors
Utilize Constructor Chaining
Prefer Small, Simple Functions
Avoid inefficient string operations
Prefer Single Large Assemblies Rather Than Multiple Smaller Assemblies
Locking and Synchronization guidelines
Minimize Thread Creation
Do not set local variables to null.

Managed Heap and Garbage Collection Overview

Memory Allocation


  • .NET components aren’t allocated out of raw memory maintained by operating system.
  • Each physical process that hosts .NET, has pre-allocated special heap called the managed heap.
  • Managed heap is directly handled by CLR.
    When the CLR is loaded, two initial heap segments are allocated—one for small objects and one for large objects, which I will refer to as the small object heap (SOH) and the large object heap (LOH), respectively.
    Allocation requests are then satisfied by putting managed objects on one of these managed heap segments. If the object is less than 85,000 bytes, it will be put on a SOH segment; otherwise it’ll be on a LOH segment. Segments are committed (in smaller chunks) as more and more objects are allocated onto them.
  • Each thread has its own stack, but heaps are typically shared by threads in a process.
  • .NET allocates memory off the managed heap.
  • .NET maintains a pointer to the next available address in the managed heap.
  • When a new object is created, it allocates the required space for the object and advances the pointer.

    Note:
    In unmanaged environments such as C++, objects are allocated off the native operating system heap. The operating system manages its memory by using a linked list of available blocks of memory. Each time the operating system has to allocate memory, it traverses that list looking for a big enough block.

  • Win32 applications have a limitation of 2 GB memory space.

    Note:

    • By definition, a 32-bit processor uses 32 bits to refer to the location of each byte of memory. 2^32 = 4.2 billion, which means a memory address that’s 32 bits long can only refer to 4.2 billion unique locations (i.e. 4 GB).
    • In the 32-bit Windows world, each application has its own “virtual” 4GB memory space. (This means that each application functions as if it has a flat 4GB of memory, and the system’s memory manager keeps track of memory mapping, which applications are using which memory, page file management, and so on.)
    • This 4GB space is evenly divided into two parts, with 2GB dedicated for kernel usage, and 2GB left for application usage. Each application gets its own 2GB, but all applications have to share the same 2GB kernel space.
    • Even when more than 4GB of memory is present, each
      process still has the normal 2GB virtual address space, and the kernel
      address space is still 2GB


De-allocation of Memory


  • De-allocation of memory and the destruction of objects are also different in .NET.

    Note:
    COM uses reference counting for De-allocation of memory. Clients that share an object have to call AddRef( ) to increment the counter. New COM objects are created with a reference count of one. When a client is done with an object, it calls Release( ) to decrement the counter. When the reference count reaches zero, the object destroys itself.

  • .NET has a sophisticated garbage-collection mechanism that detects when an object is no longer being used by clients and then destroys it.
  • .NET keeps track of accessible paths to objects in the code.
  • .NET keeps track of each new object it allocates off the managed heap and of the relationship between this object and its clients.
  • .NET updates its graph of objects and adds a reference in the graph to that object from the object that created it.
  • The entity responsible for releasing unused memory is called the garbage collector.
  • When garbage collection is triggered, the garbage collector deems every object in the graphs as garbage.
  • The garbage collector then recursively traverses each graph, going down from the roots, looking for reachable objects.
  • Every time the garbage collector visits an object, it tags it as reachable.When the garbage collector is done traversing the graphs, it knows which objects were reachable and which were not.
  • Reachable objects should be kept alive. Unreachable objects are considered garbage, and therefore destroying them does no harm.
  • Next, the garbage collector scans the managed heap and disposes of the unreachable objects by compacting the heap and overwriting the unreachable objects with reachable one.
  • The garbage collector moves reachable objects down the heap, writing over the garbage, and thus frees more space at the end for new object allocations.
  • All unreachable objects are purged from the graphs.

    Note:
    Garbage collection is usually triggered in .NET by heap exhaustion, but application shutdown also triggers garbage collection.


Object Finalization


  • .NET objects are never told when they become garbage; they are simply overwritten when the managed heap is compacted. if the object holds expensive resources, how can it dispose of and release these resources? To address this problem, .NET provides object finalization.
  • If the object has specific cleanup to do, it should implement a method called Finalize( ), defined as:
    protected void Finalize( );
  • When the garbage collector decides that an object is garbage, it checks the object metadata. If the object implements the Finalize( ) method, the garbage collector doesn’t destroy the object. Instead, the garbage collector marks the object as reachable (so it will not be overwritten by heap compaction), then moves the object from its original graph to a special queue called the finalization queue.
    A separate thread iterates over all the objects in the finalization queue, calling the Finalize( ) method on each and letting the objects do their cleanup.
  • After calling Finalize( ), the garbage collector
    removes the object from the queue.

Generations in GC


  • The .NET Garbage Collector defines generations to optimize its work.
  • Any object created since the last garbage collection operation is a generation 0 object.
  • Any object that has survived one GC operation is a generation 1 object.
  • Any object that has survived two or more GC operations is a generation 2 object.
  • The purpose of generations is to separate local variables and objects that stay around for the life of the application.
  • Generation 0 objects are mostly local variables. Member variables and global variables quickly enter generation 1 and eventually enter generation 2.
  • Every GC cycle examines generation 0 objects. Roughly 1 GC out of 10 examines the generation 0 and 1 objects. Roughly 1 GC cycle out of 100 examines all objects.
  • Think about finalization and its cost again: An object that requires finalization might stay in memory for nine GC cycles more than it would if it did not require finalization. If it still has not been finalized, it moves to generation 2. In generation 2, an object lives for an extra 100 GC cycles until the next generation 2 collection.
  • When a generation is collected, all younger generations are also collected.
  • A generation 2 garbage collection is also known as a full garbage collection.
  • From a generation point of view, large objects belong to generation 2 because they are collected only when there is a generation 2 collection.
  • Generations are the logical view of the garbage collector heap.
  • Physically, objects live on managed heap segments.

Minimize Boxing and Unboxing


  • Value types are containers for data.
  • System.Object is a single reference type, at the root of the entire object hierarchy.
  • The .NET Framework uses boxing and unboxing to bridge the gap between these two.
  • Boxing places a value type in an untyped reference object to allow the value type to be used where a reference type is expected.
  • Unboxing extracts a copy of that value type from the box.
  • Boxing converts a value type to a reference type.
  • A new reference object, the box, is allocated on the heap.
  • And a copy of the value type is stored inside that reference object.
  • Now, the box contains the copy of the value type object.
  • When you need to retrieve anything from the box, a copy of the value type gets created and returned.
  • A copy of the object goes in the box, and another
    gets created whenever you access what’s in the box.

    Even a simple statement such as this performs boxing:

    Console.WriteLine(“A few numbers:{0}, {1}, {2}”, 25, 32, 50);

    In a sense, you have generated this construct:

    int i =25;

    object o = i; // box

    Console.WriteLine(o.ToString());

    Inside WriteLine, the following code executes:

    object o;
    int i = ( int )o; // unbox
    string output = i.ToString( );

    To avoid this particular penalty, you should convertyour types tostring instances yourself before you send them to WriteLine:

    Console.WriteLine(“A few numbers:{0}, {1}, {2}”,
    25.ToString(), 32.ToString(), 50.ToString());
    Tip:
    Minimize Boxing and Unboxing to avoid temporary copies of objects


Finalize() Method Implementation

There is much more to object finalization than meets the eye. In particular, you should note that calling Finalize() is nondeterministic in time. This may postpone the release of resources the object holds and threaten the scalability and performance of the application.

Here are a number of points to be kept in mind when implementing the Finalize( ) method:


  • When you implement Finalize( ), it’s important to call your base class’s Finalize( ) method as well, to give the base class a chance to perform its cleanup:

    protected void Finalize( )
    {
    /* Object cleanup here */
    base.Finalize( );
    }

  • Make sure to define Finalize( ) as a protected method. Avoid defining Finalize( ) as a private method, because that prevents your subclasses from calling your Finalize( ) method.
  • Avoid making blocking calls, because you’ll prevent finalization of all other objects in the queue until your blocking call returns.
  • Finalization must not rely on a specific order (e.g., Object A should release its resources only after Object B does). The two objects may be added to the finalization queue in any order.
  • It’s important to call the base-class implementation of Finalize( ) even in the face of exceptions. You do so by placing the call in a try/finally statement:

    protected virtual void Finalize( )
    {
    try
    {
    /* Object cleanup here */
    }
    finally
    {
    base.Finalize( );
    }
    }
    Note:
    Because these points are generic enough to apply to every class, the C# compiler has built-in support for generating template Finalize( ) code. In C#, you don’t need to provide a Finalize( ) method; instead, you provide a C# destructor. The compiler converts the destructor definition to a Finalize( ) method, surrounding it in an exception-handling statement and calling your base class’s Finalize( ) method automatically on your behalf.
    Classes with finalizers require a minimum of two garbage collection cycles to be reclaimed. This prolongs the use of memory and can contribute to memory pressure. When the garbage collector encounters an unused object that requires finalization, it moves it to the “ready-to-be-finlized” list. Cleanup of the object’s memory is deferred until after the single specialized finalizer thread can execute the registered finalizer method on the object. After the finalizer runs, the object is removed from the queue.
    Tip:

    • Implement Finalize() method only when you have un-managed resources to release.
    • Implementing Finalize() un-necessarily causes issues as objects stay in memory for longer duration.
    • If you Implement Finalize, Implement
      Idisposable


Implement the Standard Dispose Pattern


  • A standard pattern is used throughout the .NET Framework for disposing nonmemory resources
  • The standard dispose pattern disposes resources in deterministic way.
  • The standard dispose idiom frees your unmanaged resources using the IDisposable interface when clients remember, and it uses the finalizer defensively when clients forget.
  • The root base class in the class hierarchy should implement the IDisposable interface to free resources. This type should also add a finalizer as a defensive mechanism.
  • Your class must have a finalizer if it uses nonmemory resources. The only way you can guarantee that nonmemory resources get freed properly is to create a finalizer. So create one.

    Note:
    When the Garbage Collector runs, it immediately removes from memory any garbage objects that do not have finalizers. All objects that have finalizers remain in memory. These objects are added to a finalization queue, and the Garbage Collector spawns a new thread to run the finalizers on those objects. After the finalizer thread has finished its work, the garbage objects can be removed from memory. Objects that need finalization stay in memory for far longer than objects without a finalizer. The performance panalty associated with finalization can be avoided by implementing standard Dispose pattern.

  • The implementation of your IDisposable.Dispose() method is responsible for four tasks:

    • Freeing all unmanaged resources.
    • Freeing all managed resources (this includes unhooking events).
    • Setting a state flag to indicate that the object has been disposed. You need to check this state and throw ObjectDisposed exceptions in your public methods, if any get called after disposing of an object.
    • Suppressing finalization. You call
      GC.SuppressFinalize(this) to accomplish this task.

  • If derived classes override finalize or add their own implementation of IDisposable, those methods must call the base class; otherwise, the base class doesn’t clean up properly.
  • finalize and Dispose share some of the same responsibilities.
  • virtual void Dispose( bool isDisposing ); This overloaded method does the work necessary to support both finalize and Dispose, and because it is virtual, it provides an entry point for all derived classes.
  • Derived classes can override this method, provide the proper implementation to clean up their resources, and call the base class version. You clean up managed and unmanaged resources when isDisposing is TRue; clean up only unmanaged resources when isDisposing is false. In both cases, call the base class’s Dispose(bool) method to let it clean up its own resources.
  • Here is a short sample that shows the framework of code you supply when you implement this pattern. The MyResourceHog class shows the code to implement IDisposable, a finalizer, and create the virtual Dispose method:

    public class MyResourceHog :
    IDisposable

    {
    // Flag for already disposed
    private bool _alreadyDisposed = false;

    // finalizer:
    // Call the virtual Dispose method.
    ~MyResourceHog()
    {
    Dispose( false )
    }

    // Implementation of IDisposable.
    // Call the virtual Dispose method.
    // Suppress Finalization.
    public void Dispose()
    {
    Dispose( true );
    GC.SuppressFinalize( true );
    }

    // Virtual Dispose method
    protected virtual void Dispose( bool isDisposing )
    {
    // Don’t dispose more than once.
    if ( _alreadyDisposed )
    return;
    if ( isDisposing )
    {
    // TODO: free managed resources here.
    }
    // TODO: free unmanaged resources here.
    // Set disposed flag:
    _alreadyDisposed = true;
    }
    }

  • If a derived class needs to perform additional cleanup, it implements the protected Dispose method:

    public class DerivedResourceHog :
    MyResourceHog

    {
    // Have its own disposed flag.
    private bool _disposed = false;

    protected override void Dispose( bool isDisposing )
    {
    // Don’t dispose more than once.
    if ( _disposed )
    return;
    if ( isDisposing )
    {
    // TODO: free managed resources here.
    }


  • // TODO: free unmanaged resources here.

    // Let the base class free its resources.
    // Base class is responsible for calling
    // GC.SuppressFinalize( )
    base.Dispose( isDisposing );

    // Set derived class disposed flag:
    _disposed = true;
    }
    }

    Tip:

    • If an object implements Close() or Dispose() methods, it does so because it holds an expensive, shared, native resource that should be released as soon as possible. So, do not forget to call Close() or Dispose() on classes that support it.
    • Suppress Finalization in Dispose() method
    • If your class inherits from a disposable class, then make sure that it calls the base class’s Dispose.
    • Also, if you have any member variables that
      implement IDisposable, call Dispose on them, too.


Utilize ‘using ‘and ‘TRy/finally’ for Resource Cleanup

  • Types that use unmanaged system resources should be explicitly released using the Dispose() method of the IDisposable interface. The best way to ensure that Dispose() always gets called is to utilize the using statement or a try/finally block.
    Suppose you wrote this code:

    public void ExecuteCommand( string connString, string commandString )
    {
    SqlConnection myConnection = new SqlConnection( connString );
    SqlCommand mySqlCommand = new SqlCommand( commandString,
    myConnection );

    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();
    }

  • Two disposable objects are not properly cleaned up in this example: SqlConnection and SqlCommand. Both of these objects remain in memory until their finalizers are called. (Both of these classes inherit their finalizer from System.ComponentModel.Component.)
    You fix this problem by calling Dispose when you are finished with the command and the connection:

    public void ExecuteCommand( string connString, string commandString )
    {
    SqlConnection myConnection = new SqlConnection( connString );
    SqlCommand mySqlCommand = new SqlCommand( commandString,
    myConnection );

    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();

    mySqlCommand.Dispose( );
    myConnection.Dispose( );
    }

  • That’s fine, unless any exceptions get thrown while the SQL command executes. In that case, your calls to Dispose() never happen. The using statement ensures that Dispose() is called. You allocate an object inside a using statement, and the C# compiler generates a try/finally block around each object:
    public void ExecuteCommand( string connString, string commandString )
    {
    using ( SqlConnection myConnection = new
    SqlConnection( connString ))
    {
    using ( SqlCommand mySqlCommand = new
    SqlCommand( commandString,
    myConnection ))
    {
    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();
    }
    }
    }
  • Whenever you use one Disposable object in a function, the using clause is the simplest method to use to ensure that objects get disposed of properly. The using statement generates a TRy/finally block around the object being allocated. These two blocks generate exactly the same IL:
    SqlConnection myConnection = null;

    // Example Using clause:
    using ( myConnection = new SqlConnection( connString ))
    {
    myConnection.Open();
    }

    // example Try / Catch block:
    try {
    myConnection = new SqlConnection( connString );
    myConnection.Open();
    }
    finally {
    myConnection.Dispose( );
    }

    Tip:

    • The using statement works only if the compile-time type supports the IDisposable interface. whereas finally blocks can be used for any type of cleanup operations.
    • A quick defensive as clause is all you need to safely
      dispose of objects that might or might not implement IDisposable:

    // The correct fix.
    // Object may or may not support IDisposable.
    object obj = Factory.CreateResource( );
    using ( obj as IDisposable )
    Console.WriteLine( obj.ToString( ));

  • Every using statement creates a new nested TRy/finally block.
  • Dispose() does not remove objects from memory. It is a hook to let objects release unmanaged resources. That means you can get into trouble by disposing of objects that are still in use. Do not dispose of objects that are still being referenced elsewhere in your program.

Always Use Properties Instead of Accessible Data Members


  • Properties let you expose data members as part of your public interface and still provide the encapsulation you want in an object-oriented environment.
  • Properties enable you to create an interface that acts like data access but still has all the benefits of a function. Client code accesses properties as though they are accessing public variables.

  • Any validations on data members could be made in one place by implementing properties.
    public class Customer
    {
    private string _name;
    public string Name
    {
    get
    {
    return _name;
    }
    set
    {
    if (( value == null ) ||
    ( value.Length == 0 ))
    throw new ArgumentException( “Name cannot be blank”,
    “Name” );
    _name = value;
    }
    }

    // …
    }

  • Because properties are implemented with methods, adding multithreaded support is easier. Simply enhance the implementation of the get and set methods to provide synchronized access to the data:
    public string Name
    {
    get
    {
    lock( this )
    {
    return _name;
    }
    }
    set
    {
    lock( this )
    {
    _name = value;
    }
    }
    }
  • The property syntax extends beyond simple data fields. If your type should contain indexed items as part of its interface, you can use indexers (which are parameterized properties). It’s a useful way to create a property that returns the items in a sequence:
    public int this [ int index ]
    {
    get
    {
    return _theValues [ index ] ;
    }
    set
    {
    _theValues[ index ] = value;
    }
    }

    // Accessing an indexer:
    int val = MyObject[ i ];

Prefer ‘readonly’ to ‘const’


  • C# has two different versions of constants: compile-time constants and runtime constants.
  • Compile-time constants are slightly faster, but far less flexible, than runtime constants.
  • Reserve the compile-time constants for when performance is critical and the value of the constant will never change over time.
  • You declare runtime constants with the readonly keyword. Compile-time constants are declared with the const keyword:

    // Compile time constant:
    public const int _Millennium = 2000;

    // Runtime constant:
    public static readonly int _ThisYear = 2004

    The differences in the behavior of compile-time and runtime constants follow from how they are accessed. A compile-time constant is replaced with the value of that constant in your object code. This construct:

    if ( myDateTime.Year == _Millennium )
    compiles to the same IL as if you had written this:
    if ( myDateTime.Year ==2000 )

  • Compile-time constants can be used only for primitive types (built-in integral and floating-point types), enums, or strings.
  • You cannot initialize a compile-time constant using the new operator, even when the type being initialized is a value type.
  • Compile-time constants are limited to numbers and strings.
  • Runtime constants can be of any type.
  • You must initialize runtime constants in a constructor, or you can use an initializer.
  • The final advantage of using const over readonly is
    performance: Known constant values can generate slightly more efficient code
    than the variable accesses necessary for readonly values.

Prefer the ‘is’ or ‘as’ Operators to Casts

  • Take a look at an example. You write a piece of code that needs to convert an arbitrary object into an instance of MyType. You could write it this way:
    object o = Factory.GetObject( ); // Version
    one: MyType t = o as MyType; if ( t != null ) { // work with t, it’s a
    MyType. }
    else {

    // report the
    failure. } Or, you could

    write this: object o=
    Factory.GetObject( );

    // Version two:
    try {
    MyType t;
    t = ( MyType ) o;
    if ( t != null )
    {
    // work with T, it’s a MyType.
    } else
    {
    // Report a null reference failure.
    }
    } catch
    {
    // report the conversion failure.
    }

  • The first version is simpler and easier to read. It does not have the try/catch clause, so you avoid both the overhead and the code.

  • Notice that the cast version must check null in addition to catching exceptions. null can be converted to any reference type using a cast, but the as operator returns null when used on a null reference. So, with casts, you need to check null and catch exceptions. Using as, you simply check the returned reference against null.
    Remember that user-defined conversion operators operate only on the compile-time type of an object, not on the runtime type. It does not matter that a conversion between the runtime type of o2 and MyType exists. The compiler just doesn’t know or care. This statement has different behavior, depending on the declared type of st:

    t = ( MyType ) st;
  • This next statement returns the same result, no matter what the declared type of st is. So, you should prefer as to castsit’s more consistent. In fact, if the types are not related by inheritance, but a user-defined conversion operator exists, the following statement will generate a compiler error:
    t = st as MyType;
  • The as operator does not work on value types. This statement won’t compile:
    object o = Factory.GetValue( );
    int i = o as int; // Does not compile.
  • That’s because ints are value types and can never be null. What value of int should be stored in i if o is not an integer? Any value you pick might also be a valid integer. Therefore, as can’t be used. You’re stuck with a cast.
    But you’re not necessarily stuck with the behaviors of casts. You can use the is statement to remove the chance of exceptions or conversions:

    object o = Factory.GetValue( );
    int i = 0;
    if ( o is int )
    i = ( int ) o;
  • If o is some other type that can be converted to an int, such as a double, the is operator returns false. The is operator always returns false for null arguments.
  • The is operator should be used only when you cannot convert the type using as.

Always Provide ToString()


  • This string representation of a type can be used to easily display information about an object to users.
  • The string representation can also be useful for debugging.
  • Every type that you create should provide a reasonable override of this method.
  • The System.Object version returns the name of the type.
  • Every type you create should override ToString() to provide the most common textual representation of the type.

    public class Customer
    {
    private string _name;
    private decimal _revenue;
    private string _contactPhone;
    }

  • The inherited version of Object.ToString() returns “Customer”. That is never useful to anyone.
  • Your override of Object.ToString() should return the textual representation most likely to be used by clients of that class. In the Customer example, that’s the name:

    public override string ToString()
    {
    return _name;
    }

  • Anytime the .NET FCL wants to get the string representation of a customer, your customer type supplies that customer’s name.
  • Consider using IFormattable.ToString() for more
    complex implementations.

Ensure that 0 is a valid State for Value Types


  • The default .NET system initialization sets all objects to all 0s.
  • Never create an enum that does not include 0 as a valid choice.

    public enum Planet
    {
    // Explicitly assign values.
    // Default starts at 0 otherwise.
    Mercury = 1,
    Venus = 2,
    Earth = 3,
    Mars = 4,
    Jupiter = 5,
    Saturn = 6,
    Neptune = 7,
    Uranus = 8,
    Pluto = 9
    }

    Planet sphere = new Planet();

  • sphere is 0, which is not a valid value.


Consider overriding the Equals() method for value types


  • The Equals method is provided by System.Object.
  • To use the standard implemetation of Equals, your value type must be boxed and passed as an instance of the reference type System.ValueType.
  • The Equals method then uses reflection to perform the comparision.
  • To avoid this overhead, better override Equals method.

    Ex:
    public struct Rectangle{
    public double Length;
    public double Breadth;
    public override bool Equals (object ob) {
    if(ob is Rectangle)
    return Equals((Rectangle)ob);
    else
    return false;
    }
    private bool Equals(Rectangle rect) {
    return this.Length == rect.Length && this.Breadth==rect.Breadth;
    }
    }


Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), and operator==



  • C# provides four different functions that determine whether two different objects are “equal”:

    public static bool ReferenceEquals
    ( object left, object right );
    public static bool Equals
    ( object left, object right );
    public virtual bool Equals( object right);
    public static bool operator==( MyClass left, MyClass right );

  • Two variables of a reference type are equal if they refer to the sameobject, referred to as object identity.
  • Two variables of a value type are equal if they are the same type and they contain the same contents.
  • Object.ReferenceEquals() returns TRue if two variables refer to the same objectthat is, the two variables have the same object identity.
  • Whether the types being compared are reference types or value types, this method always tests object identity, not object contents. Yes, that means that ReferenceEquals() always returns false when you use it to test equality for value types. Even when you compare a value type to itself, ReferenceEquals() returns false. This is due to boxing.

    int i = 5;
    int j = 5;
    if ( Object.ReferenceEquals( i, j ))
    Console.WriteLine( “Never happens.” );
    else
    Console.WriteLine( “Always happens.” );

    if ( Object.ReferenceEquals( i, i ))
    Console.WriteLine( “Never happens.” );
    else
    Console.WriteLine( “Always happens.” );

  • You’ll never redefine Object.ReferenceEquals() becauseit does exactly what it is supposed to do: test the object identity of two different variables.
  • static Object.Equals(). This method tests whether two variables are equal when you don’t know the runtime type of the two arguments.
  • As with ReferenceEquals(), you’ll never redefine the static Object.Equals() method because it already does exactly what it needs to do: determines whether two objects are the same when you don’t know the runtime type.

    public static bool Equals( object left, object right )
    {
    // Check object identity
    if (left == right )
    return true;
    // both null references handled above
    if ((left == null) || (right == null))
    return false;v
    return left.Equals (right);
    }


Avoid pre-allocating memory

Allocation of managed memory is a quick operation and the garbage collector has been optimized for extremely fast allocations. The main reason for preallocating memory in unmanaged code is to speed up the allocation process. This is not an issue for managed code.

Prefer Variable Initializers to Assignment Statements

Classes often have more than one constructor. In such cases, it easy for a member variables and constructors to get out of synch. The best way to make sure this doesn’t happen is to initialize variables where you declare them instead of in the body of every constructor.
Utilize the initializer syntax for both static and instance variables:

public class MyClass
{
// declare the collection, and initialize it.
private ArrayList _coll = new ArrayList( );
}

Regardless of the number of constructors you eventually add to the MyClass type, _coll will be initialized properly.
The compiler generates code at the beginning of each constructor to execute all the initializers you have defined for your instance member variables.
The initializers are added to the compiler-generated default constructor. So, even if you don’t have constructor defined explicitly, the variable is already initialized with the help of initializers.
Using initializers is the simplest way to avoid uninitialized variables in your types.

You should not use initializer when:


  1. you are initializing the object to 0, or null. The default system initialization sets everything to 0 for you before any of your code executes. The system-generated 0 initialization is done at a very low level using the CPU instructions to set the entire block of memory to 0. Any extra 0 initialization on your part is redundant and becomes inefficient.
  2. same initialization doesn’t happen on all constructors.

    public class MyClass
    {
    // declare the collection, and initialize it.
    private ArrayList _coll = new ArrayList( );

    MyClass( )
    {
    }

    MyClass( int size )
    {
    _coll = new ArrayList( size );
    }
    }

    When you create a new MyClass, specifying the size of the collection, you create two array lists. One is immediately garbage. The constructor body creates the second array list.

  3. Initialization requires exception handling. You
    cannot wrap the initializers in a TRy block.

Initialize Static Class Members with
Static Constructors


  • You should initialize static member variables in a
    type before you create any instances of that type.
  • C# lets you use static initializers and a static
    constructor for this purpose.
  • A static constructor is a special function that
    executes before any other methods, variables, or properties defined in that
    class are accessed.
  • As with instance initialization, you can use the
    initializer syntax as an alternative to the static constructor. If you simply
    need to allocate a static member, use the initializer syntax. When you have
    more complicated logic to initialize static member variables, create a static
    constructor.
  • As with instance initializers, the static
    initializers are called before any static constructors are called. And, yes,
    your static initializers execute before the base class’s static constructor.

Utilize Constructor Chaining


  • When your class is having multiple constructors
    defined, member initialization gets repeated among all the constructors. C++
    programmers would factor the common algorithms into a private helper method.
    Initializing read-only variables is not possible with this approach as it can
    be done only in the constructor.
  • When you find that multiple constructors contain the
    same logic, factor that logic into a common constructor.
  • With constructor chaining, you’ll get the benefits of
    avoiding code duplication, and constructor initializers generate much more
    efficient object code.
  • Constructor initializers allow one constructor to
    call another constructor. This example shows a simple usage:

    public class MyClass
    {
    // collection of data
    private ArrayList _coll;
    // Name
    of the instance:
    private string _name;
    public MyClass() :
    this( 0, “”
    )
    {
    }
    public MyClass( int initialCount ) :
    this( initialCount, “” )
    {
    }
    public MyClass( int
    initialCount, string name )
    {
    _coll = ( initialCount > 0 ) ?
    new ArrayList( initialCount ) :
    new ArrayList();
    _name = name;
    }
    }

Prefer Small, Simple Functions


  • Translating your C# code into machine-executable code
    is a two-step process.
  • The C# compiler generates IL that gets delivered in
    assemblies.
  • The JIT compiler generates machine code for each
    method, as needed.
  • Small functions make it much easier for the JIT
    compiler to amortize that cost.

Avoid inefficient string operations


  • Avoid inefficient string concatenation.
  • Use + when the number of appends is known.
  • Use StringBuilder when the number of appends is
    unknown.
  • Treat StringBuilder as an accumulator.
  • Use the overloaded Compare method for case
    insensitive string comparisons.

Prefer Single Large Assemblies Rather Than
Multiple Smaller Assemblies

Overheads associated with smaller
assemblies:


  • The cost of loading metadata for smaller assemblies
  • JIT Compile Time
  • Security Checks

Locking and Synchronization
guidelines


  • Acquire locks late and release them early
  • Avoid locking and synchronization unless required
  • Use granular locks to reduce contention.
  • Avoid excessive fine grained locks
  • Use the fine-grained lock(C#) statement instead of
    Synchronized
  • Avoid locking “this”

Minimize Thread Creation

Threads use both managed and unmanaged
resources and are expensive to initialize and may result in the processor
spending most of its time performing thread switches; it also places increased
pressure on the garbage collector to cleanup resources.use the ThreadPool when
you need threads.

Do not set local variables to null.

JIT compiler can statically determine that
the variable is no longer referenced and there is no need to explicitly set it
to null. And do not forget to set Unneeded Member variables to Null before
making long-running call

Hello world!

September 29, 2008

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!