Implementing .NET Coding Standards

 

asp:Feature

 

Implementing .NET Coding Standards

 

By Dr. Adam Kolawa

 

With the .NET Framework, Microsoft has taken a big step forward in making the Internet a true distributed computing platform. .NET provides a complete framework that enables computers, devices, and services to collaborate. The result is dynamic Web services that can be programmed in any language without needing to worry about interoperability issues.

 

However, while the .NET Framework is dynamic and promises to bring great advances in Web services, there are a few bumps on this rosy road. The biggest is the fact that companies and development shops that choose to migrate to .NET from their current programming language need a good deal of time to ramp up to .NET. This is not a drawback of .NET, but rather a migration issue. It takes time to learn a new way of doing things, and .NET, while dynamic and innovative, is not easy to learn.

 

This issue of migration holds many hidden pitfalls. If your development team has trouble migrating to .NET, you open your Web services applications up to security gaps, functionality lapses, maintenance problems, and other issues of quality.

 

How then, can you bring your team up to speed and not bring these problems into your applications?

 

The answer is through .NET coding standards. Having the group adhere to a basic set of .NET coding standards allows you to maintain an internal and basic group Service Level Agreement (SLA) that your .NET team must meet. It also gives programmers trying to learn .NET a way to understand what coding constructs in .NET are critical, and which are not terribly crucial for their immediate needs. As your team grows in its .NET skills, you can add further coding standards to increase the scope of the code analysis.

 

Although the examples used in this article illustrate C# coding standards, the same principles can be applied to any of the programming languages that target the .NET Framework, such as VB.NET and Managed C++. This article will consider some common pitfalls with .NET and explain the coding standards that your development team can use to prevent these problems from occurring.

 

Optimizing Performance

Developers write code and want it to perform as quickly as possible. For example, developers often try to optimize C# by using String. String is a constant object, which means that, by default, it can only be created and read, but never modified. Developers can use String when they do not think they will want to modify it as the program is executed:

 

public class MakeMessage

 {

   public string GetMessage (string[] words)

   {

       string message = "";

       for (int i = 0; i < words.Length; i++)

       {

           message += " " + words [i];

       }

       return message;

   }

 }

 

The segment of code above gets a new word every time it passes through the loop. It appears that a new message string is created and that the message is being appended, but these appearances are deceiving. In fact, the old memory under the message is being disregarded and new memory is being allocated. The old information from the message is copied to the new memory and then a new character is added at the end. The new memory created is one word longer than the old memory. The code itself is deceptive to read in this instance. Because it contains message +=, the code looks as if it will increment the message. However, the message is actually being created from scratch each time.

 

.NET developers working with C# need to understand the difference between String and StringBuilder. StringBuilder is a dynamic object. Because it can be modified, StringBuilder can truly be appended, rather than merely giving the false appearance of being appended. Developers can use StringBuilder in performance-critical sections of code, such as loops.

 

In the following example, StringBuilder is used to modify the string inside the loop:

 

public string GetMessage (string[] words)

 {

     StringBuilder message = new StringBuilder();

     for (int i = 0; i < words.Length; i++)

     {

         message.Append(" ");

         message.Append(words[i]);

     }

     return message.ToString();

 }

 

Therefore, developers should define the message as StringBuilder rather than String in situations where they will want to modify the code. Developers will then be able to use the method Append to modify the memory without creating new memory each time through the loop.

 

Optimizing Memory

Developers want to optimize memory in their applications, but how do they get rid of chunks of memory that are no longer needed?

 

The problem occurs when there are pointers in the program that refer to the chunk of memory. They are essentially forgotten pointers because the memory is no longer needed. The pointers are useless, and the developer did not intend to keep them, but they remain in the program because the developer neglected to null them.

 

In the following C# example, we have the function MakeSplashImage. We assume it calls new and uses a lot of memory. This function result can be a display or whatever else we want. We are sending the memory reference for bigSplashImage to the function that displays it. After the image is displayed, the memory is really not needed in the program:

 

public class GUI

{

 public static void Main(string[] args)

 {

     Graphics bigSplashImage = MakeSplashImage();

     DisplayMomentarily(bigSplashImage);

     while (MoreProcessInput())

     {

         Process();

     }

 }

}

 

However, notice in the above example that the reference pointer has not been nulled. Therefore, developers who want to avoid leaving unwanted references in their .NET programs should zero these references as soon as they no longer need them:

 

public class GUI

{

public static void Main (string[] args)

 {

     Graphics bigSplashImage = MakeSplashImage();

     DisplayMomentarily(bigSplashImage);

     bigSplashImage = null;

     while (MoreProcessInput())

     {

         Process();

     }

 }

}

 

Memory issues are very common when developers use temporary variables in .NET. When they forget to zero their temporary variables, they end up with what essentially amounts to a memory leak. Therefore, nullify temporary references to objects taking large amounts of memory as soon as they are no longer needed.

 

Optimizing External Resource Usage

When programming in .NET, developers need to be aware that there are many layers of code working underneath them. Though developers may be dealing mainly with high-level language performing complicated functions, the layers of code underneath that language are performing a host of other functions. These layers will behave differently depending on what is done to the code at the upper level.

 

The lower layers of code are vital to the proper functioning of an application; they are the behind-the-scenes workers that make high-level functionality possible. If these hidden layers are ignored, they will most likely come back to cause problems in the application.

 

One lower layer of code that cannot be ignored is the communication with external resources. There is a central rule that should guide all interactions with external resources. Simply put, if an external resource is opened, it should be closed as soon as it is finished being used. The following coding example deals specifically with file inputs, which are just one type of external resource. However, the lesson from this example applies to interactions with any external resources. Notice that this C# code looks as if it makes a clean exit:

 

public class ReadFile

{

 public static string Read(String path)

 {

     StreamReader reader = new StreamReader(path);

     String contents = reader.ReadToEnd();

     reader.Close();

     return contents;

 }

}

 

However, the code above is deceptive: The reader.Close command does not make a clean exit. Whether talking to a database, opening files, opening sockets, or sending instructions to the screen, developers need to close any external resources they have opened. The dangerous aspect of dealing with external resources is that when developers write a piece of code such as the aforementioned segment, it seems to run well. However, developers may encounter an error when dealing with an external resource.

 

In a case such as the above, the code will throw an exception indicating a serious problem. Exceptions can transfer control to different parts of the program. In the previous example, if the method ReadToEnd throws an exception, control will be transferred out of the method read and the file will not be closed.

 

In this situation, developers may choose to handle the exception and ignore the problem. However, they should stop to consider that the exception might have come from the interactions with external resources. If developers merely handle the exception, they will face the strong possibility of a resource leak. In other words, they will run out of resources at some point, which means they will not be able to open files or sockets and will not have access to the database.

 

If the code throws exceptions when using external resources, developers need to write a finally block. The finally block will always be executed, regardless of whether code is exited through exceptions or through normal execution. When the finally block is used, developers are guaranteed their code will clean up after them by closing all of their external resources. Therefore, developers should always exit code cleanly by using the finally block:

 

public class ReadFile

{

public static String Read(String path)

 {

     StreamReader reader = null;

     try

     {

         reader = new StreamReader(path);

          string contents = reader.ReadToEnd();

         return contents;

     }

     finally

     {

         if (reader != null)

         {

             reader.Close();

         }

     }

 }

}

 

The segment of code above is an example of how to clean up in the finally block rather than in the original code. The finally block is inserted just before the reader.Close command. Developers now know that they will be able to close the external resources and exit the code. They will also be able to open the external resources the next time they need to use them. Using the finally block guarantees that developers will not leak resources. Therefore, it is useful to write a finally block to clean up when dealing with external resources.

 

Using Implicit Cast Operators

When using implicit cast operators, developers must be aware of all potential consequences. For example, errors in the code can stem from the improper use of Vector and Figure classes in conjunction with implicit cast operators:

 

using System;

 public class Figure

 {

     public void Transform(double d)

     {

         //this method resize figure with d factor

     }

     public void Transform(Vector v)

     {

         //this method moves figure using vector

     }

 }

 public class Vector

 {

     public static implicit operator double(Vector v)

     {

         return Math.Sqrt(v.x * v.x + v.y * v.y);

     }

     private double x;

     private double y;

     public Vector(double x, double y)

     {

         this.x = x;

         this.y = y;

     }

     public static void Main()

     {

         Figure f = new Figure();

         Vector v = new Vector(1, 1);

         f.Transform(v);

     }

     //...

 }

 

As seen in the code above, the developer used the class Vector with an implicit cast operator to convert Vector into its length as double. In another part of this code, a Figure class provides methods for its Transformations.

 

In its current state, calling Transform on the Figure object with a Vector object as an argument causes figure translation. However, if another programmer removes the method Transform(Vector) and is not aware of the implicit cast operator for the Vector class, the code will behave improperly:

 

using System;

 public class Figure

 {

     public void Transform(double d)

     {

         //this method resize figure with d factor

     }

 }

 public class Vector

 {

     public static implicit operator double(Vector v)

     {

         return Math.Sqrt(v.x * v.x + v.y * v.y);

     }

     private double x;

     private double y;

     public Vector(double x, double y)

     {

         this.x = x;

         this.y = y;

     }

     public static void Main()

     {

         Figure f = new Figure();

         Vector v = new Vector(1, 1);

         f.Transform(v);

     }

     //...

 }

 

There is no error caused by the missing Transform(Vector) method. Instead, the Transform(double) method is called and Figure is scaled with vector length factor instead of being translated.

 

If a developer changes the code design so that an implicit cast operator is not necessary, the problems shown previously will not occur. For example, a developer may instead use an explicit cast operator, as shown below:

 

public static explicit operator double(Vector v)

{

 return Math.Sqrt(v.x * v.x + v.y * v.y);

}

 

Object-oriented Programming

Object-oriented programming (OOP) makes code reusable, easily maintainable, and better organized. However, there are a number of pitfalls; for example:

 

public class BankAccount

 {

   public int _balance;

 }

 

The class BankAccount is used to represent a bank account, but the variable used to represent the balance has been made public. Even though declaring the variable public is legal according to the .NET framework, it makes the code very difficult to modify and improve. There is a safer way of writing the code and achieving the same effect:

 

public class BankAccount

 {

   private int _balance;

   public int Balance

   {

       get

       {

           return _balance;

       }

       set

       {

           _balance = value;

       }

   }

 }

}

 

Here, the _balance variable has been declared private, and a public property has been defined to access it. The code is now very easy to maintain because you can change the BankAccount implementation without having to change any of the client code.

 

For example, developers can make the BankAccount object thread-safe just by adding synchronization to get/set Balance property methods. Note that none of the other methods that may be using BankAccount objects need to be modified in this case. Therefore, developers should declare their variables private.

 

Conclusion

Coding standards are a vital and important error prevention tool, but they must be used regularly, even religiously, in order to be effective. This is especially true as development projects become more and more complex in an effort to meet consumer demand for better features and more functionality. Implementing and using coding standards early, and enforcing them automatically throughout the development lifecycle, results in many outstanding product benefits.

 

Dr. Adam Kolawa is the co-founder and CEO of Parasoft, a leading provider of Automated Error Prevention (AEP) software solutions. Kolawa s years of experience with various software development processes has resulted in his unique insight into the high-tech industry and the uncanny ability to successfully identify technology trends. As a result, he has orchestrated the development of several successful commercial software products to meet growing industry needs to improve software quality - often before the trends have been widely accepted. Kolawa, co-author of Bulletproofing Web Applications (Hungry Minds, 2001), has contributed to and written over 100 commentary pieces and technical articles for publications such as The Wall Street Journal, CIO, Computerworld, Dr. Dobb s Journal, and IEEE Computer; he has also authored numerous scientific papers on physics and parallel processing. His recent media engagements include CNN, CNBC, BBC, and NPR. Kolawa holds a Ph.D. in theoretical physics from the California Institute of Technology, and has been granted 10 patents for his recent inventions. In 2001, Kolawa was awarded the Los Angeles Ernst & Young s Entrepreneur of the Year Award in the software category.

 

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish