y: How to code review application blocks using
coding standards covering functional, deployment, performance,
scalability, security, and globalization considerations.
Overview
Application Block Code Review
Tools
Summary
The examples in this chapter assume a design review of an application block named Configuration Management Application Block (CMAB). The requirements for the CMAB are the following:
Tables 5.1, 5.2, and 5.3 show the detailed test plan for the CMAB from the code review perspective.
For more information about how to create test plans for the application block, see Chapter 3, "Testing Process for Application Blocks."
For example, if you are doing the code review of the CMAB, you should watch out for the following:
Some common points that need to be handled during a code review from the naming conventions perspective are the following:
Commenting the code is important, but it should not be redundant. A block of code that follows other coding standards does not need to have comments for every line of code and will be readily understandable even without extensive commenting. However, comments are important to give the necessary information about the purpose or methodology used by the logic. Some of the factors that need to be handled while doing the code review from the commenting standards perspective are the following:
For more information about guidelines for documentation comments in C#, see "B. Documentation comments" on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfCSharpSpec_B.asp.
For more information about commenting guidelines for Visual Basic .NET, see "Comments in Code" on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcn7/html/vaconCommentsInCode.asp.
The following are some of the vulnerabilities you can look for during security code reviews:
For more information about security code reviews, see "Chapter 21Code Review" in Improving Web Application Security: Threats and Countermeasures on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh21.asp.
Before starting the code review, you should note all cultures the application block will be supporting. The common issues related to globalization features revolve mostly around a set of features. This includes features such as any type of string operations, including comparing, sorting, or indexing, formatting date and time in specific cultures, using calendars in specific cultures, or comparing and sorting data in specific cultures.
The following are some of the best practices you should review for the application blocks:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/exceptdotnet.asp.
During the code review, testers should always try to identify the usage scenarios for additional testing. These are the scenarios that require extensive testing rather than a simple code review, such as in the following examples:
The tool comes with a predefined set of rules, but you can customize and extend them.
For more information, see the following resources:
Contents
ObjectiveOverview
Application Block Code Review
Tools
Summary
Objective
- Learn the process for reviewing the implementation of application blocks, including various parameters such as performance, security, exception management, and globalization and localization features.
Overview
The implementation of application blocks should be validated against the various coding standards and best practices in the language that is used for developing the application block. The code should be reviewed from different perspectives, such as adherence to design and functional specifications, to ensure that it follows best practices related to parameters, including performance, security, globalization and localization, and exception management.The examples in this chapter assume a design review of an application block named Configuration Management Application Block (CMAB). The requirements for the CMAB are the following:
- It provides the functionality to read and store configuration information transparently in a persistent storage medium. The storage mediums are SQL Server, the registry, and an XML file.
- It provides a configurable option to store the information in encrypted form and plain text using XML notation.
- It can be used with desktop applications and Web applications that are deployed in a Web farm.
- It caches configuration information in memory to reduce cross-process communication, such as reading from any persistent medium. This reduces the response time of the request for any configuration information. The expiration and scavenging mechanism for the data that is cached in memory is similar to the cron algorithm in UNIX.
- It can store and return data from various locales and cultures without any loss of data integrity.
Application Block Code Review
This section describes the steps involved in performing a code review for an application block.Input
The following input is required for a code review:- Requirements (use cases, functional specifications, deployment scenarios, and security-related requirements for the target deployments)
Code Review Steps
The process for an application block code review is shown in Figure 5.1.
Figure 5.1. The code review process for application blocks
As shown in Figure 5.1, application block code review involves the following steps:
- Create test plans. Create test plans that list all test cases and execution details from a code review perspective.
- Ensure that the implementation is in accordance with the design. The implementation should adhere to the design decided on in the architecture and design phase.
- Ensure that naming standards are followed. The naming standards for assemblies, namespaces, classes, methods, and variables should be in accordance with the guidelines specified for the Microsoft® .NET Framework.
- Ensure that commenting standards are followed. The comments in the implementation should adhere to the standards for the language used for developing the application block.
- Ensure that performance and scalability guidelines are followed. The code should follow the implementation best practices for .NET Framework. This optimizes performance and scalability.
- Ensure that guidelines for writing secure code are followed. The code should follow the implementation best practices. This results in hack-resistant code.
- Ensure that globalization-related guidelines are followed. The code should follow globalization-related best practices in such a way that the application block can be easily localized for different locales.
- Validate exception handling in the code. The goal of exception handling should be to provide useful information to end users and administrators. This minimizes unnecessary exceptions at the same time.
- Identify the scenarios for more testing. During the white box testing phase, identify the scenarios that need more testing.
Step 1: Create Test Plans
Create detailed test plans and detailed test cases for code review from various perspectives, including performance, security, and exception management.Tables 5.1, 5.2, and 5.3 show the detailed test plan for the CMAB from the code review perspective.
Table 5.1: Sample Test case from the detailed test plan document for the CMAB
Scenario 1 | To ensure the functional mapping toward the design specification document. | |
---|---|---|
Priority | High | |
Comments | ||
1.1 | Low | To ensure the functional mapping toward the design specification document. |
Table 5.2: Sample Test case from the detailed test plan document for the CMAB
Scenario 2 | To use various tools against the application block. | |
---|---|---|
Priority | High | |
Comments | ||
2.1 | High | To validate the adherence to various programming practices by using FxCop to analyze various assemblies. |
2.4 | High | To verify that there are no politically incorrect instances in the comments by running an automated tool against the application block assemblies. |
Table 5.3: Sample Test case from the detailed test plan document for the CMAB
Scenario 3 | To review the code of the application block using various guidelines prescribed by Microsoft for .NET development. | |
---|---|---|
Priority | High | |
Comments | ||
3.1 | High | To verify that the CMAB code follows the best practices and considers the trade-offs for better performance and scalability. |
3.2 | High | To verify that the CMAB code follows the best practices and trade-offs for security features. |
3.3 | High | To verify that the CMAB code considers the best practices for globalization. |
3.4 | High | To verify that the CMAB code follows the best practices for exception management. |
For more information about how to create test plans for the application block, see Chapter 3, "Testing Process for Application Blocks."
Step 2: Ensure That the Implementation Is in Accordance with the Design
In typical development cycles, design precedes implementation. Developers write the source code in accordance with the design and the functional specification documents. The design and functional specification documents include the following:- High-level design documents. These include documents that show the component-level view of the application block. The component-level view contains the specifications about the classes in the components, the public methods (along with their signatures) in the classes and the description of the all components, the classes, and the public methods. The high level design document may also contain the description and flow of individual use cases.
- Low-level design documents. These include documents that detail information such as the class-level variables, some pseudo code to illustrate the algorithm for the functions within the class, entity relationship diagrams showing the relationship between various classes and components, and sequence diagrams showing the process flow for various use cases.
- List of assumptions and constraints. This list includes assumptions, design standards, system constraints, and implementation considerations.
For example, if you are doing the code review of the CMAB, you should watch out for the following:
- The code should maintain a design of inheritable interfaces to handle the different persistent mediums that are to be used to save the configuration settings.
- The code should consist of modules that specifically address every persistent medium that is listed in the functional specification document.
- The setting options in the configuration file for enabling and disabling caching in memory information should be implemented using parameterized methods. This ensures that a minimal amount of code can effectively address all the options and their implementations with appropriate options for scalability.
Step 3: Ensure That Naming Standards Are Followed
A good naming style is one of the parameters that help the code to be consistent and identifiable. It allows quick walkthrough and easy understanding of the code, thereby helping redress the problems in the code structure. This helps achieve process-dependent maintainability instead of people-dependent maintainability. Any developer can easily understand the functionality from the functional specifications and read through the code to see the actual implementation. This helps when a developer other than the original author fixes bugs or adds additional functionality.Some common points that need to be handled during a code review from the naming conventions perspective are the following:
- Capitalization styles. Classes, events,
properties, methods, and namespaces should use Pascal casing; whereas
parameter names for functions should use Camel casing. Pascal casing
requires the first letter in the identifier and the first letter of each
subsequent concatenated word to be capitalized. For example, the class
should be declared as shown in the following code.
//It is recommended to name a class like this public ImplementClass
The casing in the following code is not recommended for naming a class.
//It is not recommended to name a class like this public implementclass
Camel casing requires the first letter of an identifier to be lowercase. For example, a function that takes in parameters should have parameters declared as shown in the following code.
void MyFunc(string firstName, string lastName)
The casing in the following code is not recommended for the function arguments.
//It is not recommended to name the function arguments like this void MyFunc(string FirstName, string LastName)
- Case sensitivity. Check if all the code components
are fully usable from both languages that are case-sensitive and
languages that are not case-sensitive. Just because a particular
language is case-insensitive, the naming conventions should not be a mix
of different cases. Also, the identifier names should not differ by
case alone. For example, you should avoid using the following
declarations because a case-insensitive language cannot differentiate
between them.
string YourName string yourname
- Acronyms and abbreviations. Well known and
commonly used acronyms should be used in the code. However, the
identifier names should not be made into abbreviations or parameter
names as shown in the following code.
//Use this declaration void ValidateUserIdAndPwd() //Instead of this void ValUIAP()
You can use Camel casing or Pascal casing for the identifiers and parameters that have acronyms that are longer than two characters. However, you should capitalize the acronyms that consist of only two characters as shown in the following code.
//Use this declaration MyWebService.RA //Instead of this MyWebService.Ra
- Selection of words. The words used for identifiers and parameter names should not be the same as standard words or key words in the .NET Framework class library.
Step 4: Ensure That Commenting Standards Are Followed
Commenting the code is one of the most important practices that significantly increases the maintainability and usability of the application block. The developer can read through the comments to understand and customize the code for the application block to suit the application needs.Commenting the code is important, but it should not be redundant. A block of code that follows other coding standards does not need to have comments for every line of code and will be readily understandable even without extensive commenting. However, comments are important to give the necessary information about the purpose or methodology used by the logic. Some of the factors that need to be handled while doing the code review from the commenting standards perspective are the following:
- Make sure that all methods, classes, and namespaces have all the proper information such as comment headers, clearly specifying the purpose, parameters, exception handling, and part of the logic (if required) for that method.
- Make sure that there are comments for complex logic in all methods. Also make sure that these comments clearly explain the logic and its complexity. If required, you can use an example in some cases to help explain the logic.
- Make sure that there are appropriate comments to indicate why a particular variable is assigned a particular initial value. For example, if any integer variable is being initialized with a certain number, such as 0 or 1, the comment should clearly specify the relevance of the initialization.
- Make sure that you spell check comments and that proper grammar and punctuation is used.
- Make sure that the comments do not use politically incorrect words that can be considered as biased.
- Make sure you use language-specific styles for commenting the code.
//This is a single line comment /*This is a block comment spanning multiple lines */The language also allows you to add documentation comments in XML notation as shown in the following code.
/// <summary> /// This class is used for reading the configuration... /// </summary>These types of comments usually precede a user-defined type, such as a class or interface. You can compile a documentation file for these comments using a documentation generator. The following are some of the main commenting guidelines for C#:
- Avoid block comments for descriptions; you should use documentation style comments for description of the identifiers.
- Single line comments are preferred when commenting a section of code.
- The length of comment should not significantly exceed the length of code it explains.
For more information about guidelines for documentation comments in C#, see "B. Documentation comments" on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfCSharpSpec_B.asp.
For more information about commenting guidelines for Visual Basic .NET, see "Comments in Code" on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcn7/html/vaconCommentsInCode.asp.
Step 5: Ensure That Performance and Scalability Guidelines Are Followed
The code review ensures that the implementation adheres to best design practices and implementation tips that help improve performance and scalability. Some of the important things you should look for in any source code are the following:- Code ensures efficient resource management. From a
performance perspective, the first and foremost step in the code review
is to validate that the code handles its resources efficiently. The
performance of the application block significantly depends on the way
code engages and releases resources. If the code holds on to a scarce
resource for longer than required, it may result in behaviors such as
increased contention higher response times or increased resource
utilization. The following are some of the guidelines for efficient
resource management:
- The resources should be released as early as possible. This is
especially true in cases of shared resources, such as database
connections or file handles. As much as possible, the code should be
using the Dispose (or Close) pattern on disposable resources. For
example, if the application block is accessing an unmanaged resource,
such as a network connection or a database connection across client
calls, it provides clients a suitable mechanism for deterministic
cleanup by implementing the Dispose pattern. The following code sample
shows the Dispose pattern.
public sealed class MyClass: IDisposable { // Variable to track if Dispose has been called private bool disposed = false; // Implement the IDisposable.Dispose() method public void Dispose(){ // Check if Dispose has already been called if (!disposed) { // Call the overridden Dispose method that contains common cleanup // code // Pass true to indicate that it is called from Dispose Dispose(true); // Prevent subsequent finalization of this object. This is not // needed because managed and unmanaged resources have been // explicitly released GC.SuppressFinalize(this); } } // Implement a finalizer by using destructor style syntax ~MyClass() { // Call the overridden Dispose method that contains common cleanup // code // Pass false to indicate the it is not called from Dispose Dispose(false); } // Implement the override Dispose method that will contain common // cleanup functionality protected virtual void Dispose(bool disposing){ if(disposing){ // Dispose time code . . . } // Finalize time code . . . } ... }
- The code should use try-finally blocks in Visual Basic .NET or
statements in C# to ensure resources are released, even in the case of
an exception. The following code sample shows the usage of a using statement.
using( StreamReader myFile = new StreamReader("C:\\ReadMe.Txt")){ string contents = myFile.ReadToEnd(); //... use the contents of the file } // dispose is called and the StreamReader's resources released
- The classes accessing the unmanaged object should be separated out as wrapper classes for the object without referencing to other unmanaged objects. In this way, only the wrapper object is marked for finalization without other leaf objects being promoted into the finalization queue for an expensive finalization process.
- Analyze the class and structure design and identify those that contain many references to other objects. These result in complex object graphs at run time, which can be expensive for performance. Identify opportunities to simplify these structures.
- If you do not require a class level member variable after the call, check before making a long-running I/O call. If this is the case, set it to null before making the call. This enables those objects to be garbage collected while the I/O call is executing.
- Identify the instances where your code caches objects to see if there is an opportunity to use weak references. Weak references are suitable for medium-sized to large-sized objects stored in a collection. They are not appropriate for very small objects. By using weak references, cached objects can be resurrected easily, if needed, or they can be released by garbage collection when there is memory pressure. Using weak references is just one way of implementing caching policy. For more information about caching, see the "Caching" section of "Chapter 3Design Guidelines for Application Performance" in Improving .NET Application Performance and Scalability on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt03.asp.
- Ensure that the code does not call GC.Collect explicitly because the garbage collector is self-tuning; therefore, programmatically forcing garbage collection is more likely to hinder performance than to help it.
- Investigate where the buffers (required mostly for I/O calls) are allocated in your code. You can reduce heap fragmentation if they are allocated when the application block is initialized at the application startup. You should also consider reusing and pooling the buffers for efficiency.
- Consider using a for loop instead of foreach to increase performance for iterating through .NET Framework collections that can be indexed with an integer.
- The resources should be released as early as possible. This is
especially true in cases of shared resources, such as database
connections or file handles. As much as possible, the code should be
using the Dispose (or Close) pattern on disposable resources. For
example, if the application block is accessing an unmanaged resource,
such as a network connection or a database connection across client
calls, it provides clients a suitable mechanism for deterministic
cleanup by implementing the Dispose pattern. The following code sample
shows the Dispose pattern.
- Code performs string operations efficiently.
String operations in managed code look deceptively simple, but they can
be a cause of memory overhead if they are not handled efficiently. The
reason behind this is that the strings are immutable. If you concatenate
(or perform some other operation that changes the string itself) a
string to another string, the old object has to be discarded with the
new value being stored in a new object. To minimize the affect on these
side effect allocations, especially in loops, you should ensure that the
following guidelines are followed:
- StringBuilder is used when the number of concatenations is unknown, such as in loops or multiple iterations. Concatenation using the + operator may result in a lot of side effect allocations that are discarded after each new iteration. The usage of StringBuilder is shown in the following code.
StringBuilder sb = new StringBuilder(); Array arrOfStrings = GetStrings(); for(int i=0; i<10; i++){ sb.Append(arrOfStrings.GetValue(i)); }
- If you know the number of appends and concatenate strings in a single statement or operation, give preference to the + operator.
- Watch out for code that calls the ToLower method.
Converting strings to lowercase and then comparing them involves
temporary string allocations. This can be very expensive, especially
when comparing strings inside a loop. You should give preference to
using the String.Compare method. If you need to perform case-insensitive string comparisons, you can use the overloaded String.Compare method shown in the following code.
// The last argument is to signify a case-sensitive or case-//insensitive comparison String.Compare (string strA, string strB, bool ignoreCase);
For more information about CLR profiler, see "How To: Use CLR Profiler" in Improving .NET Application Performance and Scalability on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp. - StringBuilder is used when the number of concatenations is unknown, such as in loops or multiple iterations. Concatenation using the + operator may result in a lot of side effect allocations that are discarded after each new iteration. The usage of StringBuilder is shown in the following code.
- Code uses efficient data structures. The data structures used in the code minimize the overhead of enumeration and provide appropriate functionality for searching and sorting. For example, if you are enumerating through the value types, you should implement the enumerator pattern for a custom collection to avoid the overhead of boxing or unboxing.
- For a question-based review of the managed code to ensure adherence to best practices, see "Chapter 13Code Review: .NET Application Performance" in Improving .NET Application Performance and Scalability on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/ScaleNetChapt13.asp.
- For a checklist-based review of the managed code, see "Checklist: Managed Code Performance" in Improving .NET Application Performance and Scalability on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/ScaleNetCheck06.asp.
- For more information about the enumerator pattern, see "Chapter 5Improving Managed Code Performance" in Improving .NET Application Performance and Scalability on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp.
Step 6: Ensure That Guidelines for Writing Secure Code Are Followed
Security code reviews help you identify areas in your code that do not follow the best practices for writing secure code. Identifying this code is important because it directly weakens the security in one way or other. The security vulnerabilities that get introduced because of they do not adhere to guidelines for writing secure code are difficult to catch during black box testing and may surface later as a threat in a production scenario.The following are some of the vulnerabilities you can look for during security code reviews:
- Check for hard coded information. There should not be any hard coded information, such as UserName, Password, or ConnectionString, in any portion of the source code. This information should be kept in a configuration file.
- Code reviews should ensure that the code uses code access security appropriately, as discussed in the following examples:
- During the code review, make sure that the privileged code identified during the design and build phase requests appropriate permissions while accessing secure resources or while performing privileged operations.
- Review that the permission requests are appropriate and minimum. For example, if your code needs to read a specific key in the registry, it should request read permission to that specific key in the registry.
- Check whether the assembly is strong named; make sure that it does not use the AllowPartiallyTrustedCallersAttribute (APTCA). In cases where it is must use APTCA, make sure that the appropriate code access security demands are used.
- Check for a cross-site scripting attack. Never place too much trust on the data entered by the user; always validate it. Use regular expressions to validate data in ASP.NET applications.
- Check for buffer overflows. Validate length and data type of parameters and calls to the unmanaged code (including Win32 DLLs and COM objects) through COM interop or P/Invoke layers.
- Disable detail error messages and tracing. Check whether the detailed error messages are disabled from popping up on the client computer and tracing is disabled for the ASP.NET applications.
- View state protection. Check whether the view state is enabled only when required. Also check that view state protection is enabled as applicable.
For more information about security code reviews, see "Chapter 21Code Review" in Improving Web Application Security: Threats and Countermeasures on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh21.asp.
Step 7: Ensure That Globalization-related Guidelines Are Followed
Globalization is the process of ensuring that a piece of software supports localized versions of the user interface, such as messages shown to the user (such as exceptions, warnings, and information) and that it can operate on regional data (such as different date notations and currency formatting).Before starting the code review, you should note all cultures the application block will be supporting. The common issues related to globalization features revolve mostly around a set of features. This includes features such as any type of string operations, including comparing, sorting, or indexing, formatting date and time in specific cultures, using calendars in specific cultures, or comparing and sorting data in specific cultures.
The following are some of the best practices you should review for the application blocks:
- Preferably, the application should use Unicode internally. It is preferable for the application block to use Unicode encoding, though other techniques, such as DBCS (Double-Byte Character System), are also available. If you plan to use a database, you should use Unicode-compatible data types for database design. For example, the CMAB stores configuration information in a SQL Server database. This information can be from different character sets; therefore, you should make sure that the database design uses data types such as nchar, nvarchar, and ntext.
- Ensure that the culture-aware classes provided by the System.Globalization
namespace are used to manipulate and format data. The various rules for
languages and countries, such as number formats, currency symbols, and
sort orders, are aggregated into a number of standard cultures. In the
.NET Framework, culture is represented programmatically using the System.Globalization.CultureInfo class.
The .NET Framework uses a combination of two cultures to handle
different aspects of localization, namely Current culture and Current
user interface culture. Current culture determines how various data
types are formatted, such as numbers and dates. Current user interface
culture determines which localized resource file the resource manager
loads. The code may set the culture in the configuration file as shown
in the following code.
<configuration> <system.web> <globalization culture="en-US" uiCulture="de-DE"/> </system.web> </configuration>
The code may also set the culture programmatically as shown in the following code.
using System.Globalization; using System.Threading; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture ("de"); // Set the user interface culture to the browser's accept language Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
- For sorting, check if the application block uses the SortKey class and the CompareInfo class.
// Creates a SortKey using the en-US culture. CompareInfo myComp_enUS = new CultureInfo("en-US",false).CompareInfo; SortKey mySK1 = myComp_enUS.GetSortKey( "llama" );
- For string comparisons, the CompareInfo class should be used.
// Defines the strings to compare. String myStr1 = "This is one string"; String myStr2 = "This is another string"; // Creates a CompareInfo that uses the InvariantCulture. CompareInfo myComp = CultureInfo.InvariantCulture.CompareInfo; ConsoleWriteLine(" The result of case insensitive comparison is: " + myComp.Compare( myStr1, myStr2, CompareOptions.IgnoreCase));
- Formatting should be done using the appropriate classes, such as the DateTimeFormatInfo class for date and time formatting and the NumberFormatInfo class for numeric formatting. The following code shows an example of using DateTimeFormatInfo.
DateTimeFormatInfo dfmt = Thread.CurrentThread.CurrentCulture.DateTimeFormat; dfmt.LongDatePattern = @"\[yyyy-MMMM-dd\]"; string longDateString = DateTime.Now.ToLongDateString();
- If you need to convert from a string to a DateTime data type, you should use the DateTime.Parse method as shown in the following code.
DateTime dt = DateTime.Parse(aFrenchDateString, new CultureInfo("de"));
- If your application block is to be integrated with a Web
application that can be accessed from different time zones, you should
use the universal time feature provided by the .NET Framework. Use the
universal time feature for internal storage of the configuration values
that can be converted into local time zone values upon retrieval. Use
the DateTime.ToUniversalTime and DateTime.ToLocalTime methods for performing these conversions.
DateTime localDateTime = System.DateTime.Now; System.DateTime univDateTime = localDateTime.ToUniversalTime();
- Check if the application block can read and write data to and from a variety of encodings by using the encoding classes in the System.Text
namespace. Ensure that the code does not assume ASCII data and that it
assumes international characters will be supplied anywhere a user can
enter text. For example, in the CMAB, international characters should be
accepted in server names, directories, file names, and so on. You can
use the static GetEncoding method of the System.Text.Encoding class to retrieve an Encoding object for a specific Code-Page, and then use its GetBytes method to convert Unicode strings to a Code-Page specific byte array.
Encoding iso = Encoding.GetEncoding("iso8859-1"); Encoding unicode = Encoding.UTF8; byte[] unicodeBytes = unicode.GetBytes(src); iso.GetString(unicodeBytes);
- Whenever possible, check if strings are handled as entire strings instead of as a series of individual characters. This is especially important when sorting or searching for substrings. This will prevent problems associated with parsing combined characters.
- The text should be displayed using the classes provided by the System.Drawing namespace.
- For consistency across operating systems, make sure that the code does not allow user settings to override CultureInfo. The code should use the CultureInfo constructor that accepts a useUserOverride parameter and set it to false. This is most useful when using application blocks in an application that is not Web based. For Web-based applications, the effective settings are that of the ASP.NET process.
- If a security decision is based on the result of a string comparison or case change operation, check if the code is performing a culture-insensitive operation by explicitly specifying the CultureInfo.InvariantCulture property. This practice ensures that the result is not affected by the value of CultureInfo.CurrentCulture.
- For more information about satellite assemblies, see "Creating Satellite Assemblies" in the.NET Framework Developer's Guide on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcreatingsatelliteassemblies.asp. - For more information about best practices for globalization, see "Best Practices for Developing World-Ready Applications" in the.NET Framework Developer's Guide on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconbestpracticesforglobalapplicationdesign.asp. - For more information about best practices for globalization and
localization, see "Best Practices for Globalization and Localization" on
MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsent7/html/vxconBestGlobalizationPractices.asp. - For more information about issues related to globalization and
localization, see "Globalization and Localization Issues" on MSDN at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsent7/html/vxconGlobalizationLocalization.asp.
Step 8: Validate Exception Handling in the Code
Robust exception handling is a very important feature that significantly affects the user experience with the application. It helps administrators managing an application and it helps developers to analyze problems that surface in the actual production environment. Exception handling is an important design feature. During the design phase, you capture all the possible exceptions that need to be handled for a particular use case. The code review makes sure that the code appropriately handles the exceptions. The following are some guidelines for exception handling that should be reviewed in the code:- Exception handling should not be used to control the application
logic. It should be used only in exceptional cases because throwing
exceptions is expensive. For example, if the CMAB is checking for
configuration information from the database for a particular key and
does not find any value, this is an expected condition and is not a fit
case for throwing exception as shown in the following code.
// Bad scenario to throw exception static void ConfigExists( string KeyForConfig) { //... search for Key if ( dr.Read(KeyForConfig) ==0 ) // no record found { throw( new Exception("Config Not found")); } }
These conditions should be handled by returning a simple Boolean value. If the Boolean value is true, the configuration information exists in the database.
static void ConfigExists( string KeyForConfig) { //... search for key if ( dr.Read(KeyForConfig) ==0 ) // no record found { return false; } . . . }
Returning error information using an enumerated type instead of throwing an exception is another commonly used programming technique in performance-critical code paths and methods. - All exception conditions identified for a particular use case
should be handled. You should make sure the exceptions are not caught
only to be rethrown to the user because throwing exceptions is
expensive. It should be done only if you are adding some value or
performing some action on the exception object, such as logging into an
event sink.
try { //try to load an XML file LoadXmlFile(xmlApplicationDocument, _applicationDocumentPath); } catch (XmlException) { //Log the exception before re throwing it LogExceptionInSink(XmlException) //if the loading of the document fails throw a configuration //exception stating that configuration document is invalid throw new ConfigurationException(Resource.ResourceManager["RES_ExceptionInvalidConfigurationDocument"]); } finally { // Code that gets run always, whether or not // an exception was thrown. This is usually // clean up code that should be executed // regardless of whether an exception has // been thrown. }
- Code does not swallow exceptions or inefficient code that catches, wraps, and rethrows exceptions for no valid reason. If you do not need to act on a particular exception, you should allow the exception to bubble up from the application block code to the client application. The user integrating the application block can capture the exception and decide on how to act on the exception.
- Make sure that the finally blocks are used to
ensure that the resources are cleaned up. Make sure of this even in the
case of exception as shown in the following code.
SqlConnection conn = new SqlConnection("..."); try { conn.Open(); // Some operation that might cause an exception // Calling Close as early as possible conn.Close(); // ... other potentially long operations } finally { if (conn.State==ConnectionState.Open) conn.Close(); // ensure that the connection is closed }
You can also use the using statement in C# to clean up resources that require a call to Dispose ()method for deterministic clean up of resources. - Preferably, the logging mechanism should be configurable in such a way that the client can decide the event sinks for logging of particular type of errors. For example, the default configuration of the CMAB logs high severity errors in the event log and logs all the warnings and informational messages in Windows Event Trace. The configuration should also provide an option of switching on and off the logging either partially or completely. This is because logging might be an undesirable overhead in the production environment.
- Check whether the exceptions caught by any part of the program or subprogram are propagated through the system and necessary actions are taken, depending on the exception type. Functions should avoid returning error codes to the calling functions; instead, an exception propagation mechanism should be used.
- Make sure that the code uses custom exceptions. The .NET platform gives provision for creating custom exceptions. Custom exceptions generally help in giving more detailed information on the exception that occurred or in highlighting any business logic discrepancies. For example, if the CMAB fails to read the configuration file during the application initialization process, it throws an exception of type ConfigurationException or a class derived from ConfigurationException.
- Check whether the unhandled exceptions are properly managed. For
example, in ASP.NET applications, the application's Web.config file can
be used to show a custom error page for the clients instead of showing
the exact stack trace of the error to the clients by the following
settings.
<customErrors defaultredirect="http://hostname/error.aspx" mode="on"> </customErrors>
- Exception management should also ensure that any informational messages are also logged. These events are useful in monitoring the health of the application block. For example, the CMAB can report any requests that hold locks for writing or updating information for more than 3 milliseconds.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/exceptdotnet.asp.
Step 9: Identify the Scenarios for More Testing
During the code review process, testers get to review the code extensively through various perspectives, such as performance, security, and globalization. During this review, testers make sure the code is in accordance with the guidelines; however, this is not a substitute for actually testing the scenario. The goal of the review is to minimize surprises in the later stages.During the code review, testers should always try to identify the usage scenarios for additional testing. These are the scenarios that require extensive testing rather than a simple code review, such as in the following examples:
- You have done a code review of an internal function of a component that takes numeric input. You would want to make sure that during white box testing, the function is thoroughly tested for all types of inputs such as 0, -ve numbers, numbers out of the accepted range, and decimals.
- During the code review, you come across a function that makes remote calls. You may want to profile the time taken by the function when the block is under a concurrent load of users. If the remote call is an I/O call for retrieving information in buffers, you may want to further load test it with different payloads to avoid the possible problems of buffer overflow.
- A particular function does the sorting of string data before returning the values in an array. This function needs additional testing for various cultures that have been specified in the requirements.
Tools
The FxCop tool can be used to check for compliance of code with various types of guidelines. The tool analyzes binary assemblies (not source code) to ensure that they conform to the .NET Framework Design Guidelines, available as rules in FxCop.The tool comes with a predefined set of rules, but you can customize and extend them.
For more information, see the following resources:
- To download the FxCop tool, see "About Developing Reusable Libraries" on GotDotNet at:
http://msdn.microsoft.com/en-us/library/bb429476(VS.80).aspx. - For general information about FxCop, see the "FxCop Team Page" on GotDotNet at: http://msdn.microsoft.com/en-us/library/bb429476(VS.80).aspx.
- To get help and support for the FxCop, see the GotDotNet message boards for discussions about the FxCop tool at:
http://msdn.microsoft.com/en-us/library/bb429476(VS.80).aspx.
Your blog is superb, what a great idea!
ReplyDeletevoyance gratuite par Email
Very nice blog... code analysis tools C# is also very helpful tool for code analysis.
ReplyDeletePretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon. Big thanks for the useful info. z code system free trial
ReplyDelete