Improving .NET Application Performance Part 3: Class Design Considerations

In this third article of our series on improving .NET application performance and in this article we’ll discuss various techniques to optimize and improve the performance of your managed code. Most importantly; class design, memory management and garbage collection, efficient multithreading, effective asynchronous execution, boxing, and exception handling. 

As you are well aware, the way you write your managed code can either take full advantage of the common language runtime (CLR) high performance features, or it can hinder it. We will point out the most important performance-related issues that help avoid common mistakes and hopefully help you to improve the performance of your code.  In this article we’ll focus on class design considerations and have listed the most important ones below.

Avoid making classes thread safe by default – Consider if it is necessary to make your class thread safe as it is often only necessary at a higher layer and not a the individual class level.

Use the sealed keyword – If you don’t need or want to extend your base classes, consider marking them with the ‘sealed’ keyword. If you derive a class from a base class with virtual members and want to prevent further extensibility of the derived class, use the ‘sealed’ keyword with the virtual members of your derived class. Sealing virtual methods allows for inlining and other compiler optimizations.

public class MyClass{


  protected virtual void SomeMethod() { ... } 

Overdiding and seal a method in a derived class.

public class DerivedClass : MyClass { 
  protected override sealed void SomeMethod () { ... } 

Avoid unnecessary virtual members – Virtual members are more expensive to call because of the virtual lookup table and don’t work for certain runtime optimizations.

Use overloaded methods – Instead of using a method with a variable number of parameters, use overloaded methods instead. Just be aware that COM clients can’t work with overloaded methods. In that case use methods with different names.

//method taking variable number of arguments

void GetCustomers (params object [] filterCriteria)


//overloaded methods

void GetCustomers (int countryId, int regionId)

void GetCustomers (int countryId, int regionId, int CustomerType)

Overriding the Equals method – overriding the Equals method that is provided by System.Object can further improve performance. An Equals method that is specific to your value type can do a comparison much more cheaply then using the standard implementation which uses Reflection to perform the comparison.

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

Accessing a Class Property – Properties look like fields but they are not and have a hidden cost. Be aware that if you access a property additional code might be executed and accessing a property might be slower than accessing a field directly. Of course, the additional code behind a property is generally there for good reason; for example, to validate data.

If your object is designed for remote access, use methods with multiple parameters instead of requiring the client to set multiple properties or fields, reducing round trips. Also, it is considered bad practice to use properties that hide complex business rules or other costly operations; callers of properties generally expect the cost to be inexpensive.

Private vs. Public Member Variables – Avoid unnecessary public members to prevent any serialization overhead when you use the XmlSerializer class. As you well know, this class serializes all public members by default.

Limit the Use of Volatile FieldsLimit the use of the volatile keyword because volatile fields restrict the way the compiler reads and writes the contents of the field. The compiler generates the code that always reads from the field’s memory location instead of reading from a register that may have loaded the field’s value. This means that accessing volatile fields is slower than nonvolatile ones because the system is forced to use memory addresses rather than registers.

These are the most important class design considerations that can help you to optimize your managed code. In our next article we’ll focus on Garbage Collection.