Monday, October 18, 2010

General Coding Optimization Techniques


  1. Projects and Solutions

    1. In debug mode, compile with Enable Build Warnings option, disable optimizations, and enable integer overflow checks.
    2. In release mode, compile with Treat Compiler Warnings option, enable optimizations, and disable integer overflow checks.
    3. Disable incremental builds.

2. Inheritance

a.    Don't create object hierarchies more than three levels deep.
b.    Use Base suffix for abstract classes.
c.    Use compound name for derived class if possible
d.    In base classes, use abstract members to implement features that are mandatory in derived classes.
e.    Use sealed/NotInheritable keywords for types that shouldn't be derived from.
f.     Use a static class for types that are never instantiated or derived from.
g.    Abstract classes must have a constructor with protected scope.
h.    Mark members with virtual/Overridable keyword only if you know that they might be overridden in derived classes.
i.      If a member is overloaded, mark only the most complete version with the virtual/Overridable keyword.
j.      Override the ToString method to provide a textual representation of an object.
k.    Consider overriding the Equals method if two instances of a type might be considered as equal in some cases.
l.      Consider providing strong-typed versions of the Equals and Compare static methods.
m.   Don't inherit a type from MarshalByRefObject unless you strictly need its additional features.


 5. Fields and Variables

a.    Mark a field as read-only if it isn't supposed to change after instantiation.
b.    Never define public instance fields; instead, expose public or protected properties that wrap a private field.
c.    Mark all public static fields as read-only (or use a constant if possible).
d.    Declare all local variables at the beginning of the method, except block variables and variables wrapped by properties.
e.    Initialize fields in the constructor if initialization order is significant.
f.     Avoid the = (assignment) operator inside expressions.

 6. Properties

a.    Use a read-only property only if returning a private non-array field or the result of a simple calculation; in all other cases, use a method.
b.    Never define write-only properties; use Set-prefixed methods instead.
c.    Use the value keyword only inside the set block of a property.

 7. Methods

a.    Avoid methods longer than 50 executable statements.
b.    Avoid methods with more than 6 arguments.
c.    Define a single exit point for all methods, except special cases that are dealt with at the top of the method.
d.    Exit a function with the Return keyword
e.    Use parenthesis to enclose an expression returned by a function; don't use parenthesis to return a single value.
f.     Avoid overloading of a binary operator in a reference type.
g.    Don't modify arguments passed to a method that overloads an operator.
h.    Throw an exception from inside a constructor if one or more arguments aren't valid.
i.      If a type has a Finalize method, attempt to assign all fields before throwing an exception from inside a constructor.
j.      Ensure that you catch all exceptions in static constructors.

 8. Execution Flow

a.    Use curly braces also for if, else if, for, foreach, and while blocks that contain a single line.
b.    Don’t nest conditional and loop blocks to more than three levels, or four levels in exceptional cases.
c.    Avoid using the ? : ternary operator inside expressions
d.    Don't use the goto statement except to jump to another case block or to the default block in a switch statement.
e.    Don't use a floating-point variable as the controlling variable of a for loop
f.     Avoid unsafe code if possible; if you must use unsafe code, place all your unsafe methods in a DLL.

 9. Exception handling

a.    Don't throw exceptions to control execution flow, e.g. exit a procedure prematurely.
b.    Throw exceptions that are as specific as possible
c.    Don’t catch SystemException and ApplicationException objects.
d.    Don’t catch the following exceptions except for logging reasons: OutOfMemoryException, StackOverflowException, ThreadAbortException, and ExecutionEngineException.
e.    A custom exception type must inherit from ApplicationException and be marked with the Serializable attribute.


10. Numeric types

a.    Use CLS-compliant integer types if possible.
b.    Use shortened notation (e.g. x+=1) for simple increment/decrement operations.
c.    Use ++ and -- operators only inside expressions located in performance-critical code..
d.    Avoid using ++ and -- operators on a variable that appears more than once in the expression.
e.    Use a series of multiplications instead of exponentiation if the exponent is a small integer.

11. Strings

a.    Use resource files for strings that are used in the user interface.
b.    Use Char variables for one-char strings.
c.    Explicitly initialize strings to "" value.
d.    String methods and properties must return an empty string rather than a null string if the result has no characters.
e.    Always check that string arguments aren’t null object references before using them.
f.     Use the String.Compare method to compare strings in case-insensitive mode.
g.    Use the CompareOrdinal static method when comparing strings for equality in case-sensitive, locale-independent mode.
h.    Use the StringBuilder object to concatenate a variable number of strings or to concatenate strings inside a loop.
i.      Initialize the StringBuilder’s internal buffer if you know in advance a reasonable upper limit to the size of the result.
j.      Don’t append more characters to a StringBuilder after you’ve invoked its ToString method.
k.    Read a string from the configuration file if its value might change after deployment.

12. Memory usage

a.    Objects that are meant to live for the entire duration of the application lifetime should be created as early as possible.
b.    Don't box value-typed objects unnecessarily.
c.    Don’t explicitly set an object reference to null when you don’t need it any longer inside a method.
d.    Explicitly set an object reference to null if you are inside a loop and want the .NET runtime to collect the object before the loop ends.

13. ADO.NET Programming

a.    Always protect database operations from unhandled exceptions.
b.    Open a connection in a Try block and close it in the corresponding Finally block.
c.    Adopt Windows authentication to SQL Server security in Windows Forms applications.
d.    Never use the sa account when adopting SQL Server security
e.    Use an autoincrementing (identity) integer column as the primary key of a table if possible, selecting a large enough precision.
f.     Use the Command.ExecuteReader method with the CommandBehavior.SingleRow argument if the result includes max one row.
g.    Use the Command.ExecuteScalar method when the result is a single value.
h.    Consider using the TOP keyword or the SET ROWCOUNT statement to limit the number of rows returned by an SQL statement
i.      Use the CommandBehavior.CloseConnection argument when creating a DataReader object returned by a method.
j.      Invoke the Cancel method of the Command object before closing a DataReader, if you don’t want to read any remaining rows.
k.    Consider using a stored procedure for better performance and security, to execute a batch of commands, when clients can use multiple access technologies (e.g. ADO and ADO.NET).
l.      Close the DataReader object before attempting to read the return value or the output arguments of the stored procedure.
m.   Use CommandBehavior.SequentialAccess with the ExecuteReader method if you read one or more large binary or text columns.
n.    Use a DataTable instead of a DataSet object if possible.
o.    Don't rely on the DataAdapter's autoconnect feature; always explicitly open and close the connection.
p.    Never use CommandBuilder objects.
q.    Set both the AutoIncrementSeed and the AutoIncrementStep property to 1 for columns that correspond to identity fields in Access and SQL Server.
r.     Avoid passing large DataSet objects to remote components.
s.     Open an ADO.NET or COM+ serializable transaction when reading two or more tables in a master-detail relationship.
t.      Close a transaction as soon as possible. Neither accept user input while a transaction is open nor allow the user to decide whether a transaction should be committed or rolled back.
u.    Don’t rely on the default behavior of the DataAdapter object for managing update conflicts.
v.    Use the DataSet.GetChanges method to create a new DataSet pass this new object to the DataAdapter.Update method.

14. Web services

a.    Call a Web service synchronously only from server-side applications and programs with no user interface
b.    Call a Web service asynchronously from inside Windows Forms clients.
c.    Implement one-way Web service calls for lengthy methods if possible.
d.    Use the most recent version of Web Service Enhancements (WSE) to implement advanced functions.
e.    Provide a meaningful description in the WebMethod attribute.

Overloading Operators

You can add operators to your own types, allowing them to be used much like the operators with the built-in C# types.
            To understand the need for operator overloading, imagine that you need to perform matrix math operations in your program. You could instantiate a couple 2-dimensional arrays and do what you need. However, add the requirement for the matrix behavior to be reusable.& Because you need to do the same thing in other programs and want to take advantage of the fact that you have already written the code, you will want to create a new type.
            So, you create a Matrix type, which could be a class or a struct. Now consider how this Matrix type would be used. You would want to initialize two or more Matrix instances with data and then do a mathematical operation with them, such as add or get a dot product. To accomplish the mathematical operation, you could implement an Add(), DotProduct(), and other methods to get the job done. Using the classes would look something like this:

Matrix result = mat1.Add(mat2);  // instance
or
Matrix result = Matrix.Add(mat1, mat2);  // static
or even worse
Matrix result = mat1.DotProduct(mat2).DotProduct(mat3); // and so on...
            The problem with using methods like this is that it is cumbersome, verbose, and unnatural for the problem you are trying to solve. It would be much easier to have a + operator for the add operation and a * operator for the dot product operation. The following shows how the syntax appears using operators:

Matrix result = mat1 + mat2;
or
Matrix result = mat1 x mat2;
or even better
Matrix result = mat1 x mat2 x mat3 x mat4;
            This is much more elegant and easier to work with. For a single operation, one could argue that the amount of work to implement one syntax over the other is not that great. However, when chaining multiple mathematical operations, the syntax is much simpler. Additionally, if the primary users of your type are mathematicians and scientists, operators are more intuitive and natural.

When Not to Use Operator Overloading

            A lot of the discussion, so far, has emphasized the need to write code and implement types in the simplest and most natural way possible. A very important concept to remember is that although operators are simple, they are not always natural. In the example above it made sense to use operators with the Matrix type. This is similar to the reason why operators make sense with the built-in types such as int and float. However, it is easy to abuse operators and create convoluted implementations that are hard for anyone, including the original author, to understand.
            For an example of a bad implementation, consider a Car class that needs an implementation allowing you to park the car in a garage. It would be a mistake to think that the following implementation was smart:
Car mySedan = new Car();
Garage parkingGarage = new Garage();
mySedan = mySedan + parkingGarage; // park car in the garage
This is bad code. If you ever have the temptation to do something like this - don't. No one will truly understand what it means and they will not think it is clever. Furthermore, it hurts the maintainability of the application because it is so hard to understand what the code does. Although the comment is there, it doesn't help much and if it wasn't there, it would be even more difficult to grasp the concept of adding a ding a Car and a Garage.
The idea is this: Use operators where they lend understanding and simplicity to a type. Otherwise, do not use them.

Implementing an Overloaded Operator

            The syntax required to implement an overloaded operator is much the same as a static method with a couple exceptions. You must use the operator keyword and specify the operator symbol being overloaded. Here's a skeleton example of how the dot product operator could be implemented:
public static Matrix operator *(Matrix mat1, Matrix mat2)
{
    // dot product implementation
}
            Notice that the method is static. Use the keyword operator after specifying the return type, Matrix in this case. Following the operator keyword, the actual operator symbol is specified and then there is a set of parameters to be operated on. See Listing for a full example of how to implement and use an overloaded operator.
using System;
class Matrix
{
        public const int DimSize = 3;
        private double[,] m_matrix = new double[DimSize, DimSize];

        // allow callers to initialize
        public double this[int x, int y]
        {
                get { return m_matrix[x, y]; }
                set { m_matrix[x, y] = value; }
        }

        // let user add matrices
        public static Matrix operator +(Matrix mat1, Matrix mat2)
        {
                Matrix newMatrix = new Matrix();

                for (int x=0; x < DimSize; x++)
                        for (int y=0; y < DimSize; y++)
                                newMatrix[x, y] = mat1[x, y] + mat2[x, y];

                return newMatrix;
        }
}

class MatrixTest
{
        // used in the InitMatrix method.
        public static Random m_rand = new Random();

        // test Matrix
        static void Main()
        {
                Matrix mat1 = new Matrix();
                Matrix mat2 = new Matrix();

                // init matrices with random values
                InitMatrix(mat1);
                InitMatrix(mat2);
                
                // print out matrices
                Console.WriteLine("Matrix 1: ");
                PrintMatrix(mat1);

                Console.WriteLine("Matrix 2: ");
                PrintMatrix(mat2);

                // perform operation and print out results
                Matrix mat3 = mat1 + mat2;

                Console.WriteLine();
                Console.WriteLine("Matrix 1 + Matrix 2 = ");
                PrintMatrix(mat3);

                Console.ReadLine();
        }

        // initialize matrix with random values
        public static void InitMatrix(Matrix mat)
        {
                for (int x=0; x < Matrix.DimSize; x++)
                        for (int y=0; y < Matrix.DimSize; y++)
                                mat[x, y] = m_rand.NextDouble();
        }

        // print matrix to console
        public static void PrintMatrix(Matrix mat)
        {
                Console.WriteLine();
                for (int x=0; x < Matrix.DimSize; x++)
                {
                        Console.Write("[ ");
                        for (int y=0; y < Matrix.DimSize; y++)
                        {
                                // format the output
                                Console.Write("{0,8:#.000000}", mat[x, y]);

                                if ((y+1 % 2) < 3)
                                        Console.Write(", ");
                        }
                        Console.WriteLine(" ]");
                }
                Console.WriteLine();
        }
}
            Similar to the skeleton example of the dot product operator, the Matrix class in Listing 18-1 contains an operator overload for the + operator. For your convenience, I've extracted the pertinent overload implementation in the code below:
// let user add matrices
        public static Matrix operator +(Matrix mat1, Matrix mat2)
        {
                Matrix newMatrix = new Matrix();

                for (int x=0; x < DimSize; x++)
                        for (int y=0; y < DimSize; y++)
                                newMatrix[x, y] = mat1[x, y] + mat2[x, y];

                return newMatrix;
        }
            The operator is static, which is the only way it can and should be declared because an operator belongs to the type and not a particular instance. The return type is Matrix - its enclosing type. What designates this as an operator is the use of the keyword operator symbol. The parameter types are both of the enclosing type, Matrix. The implementation of the operator overload creates a new instance of the return type and performs a matrix add.
            Basically, it is that easy. There are just a few rules you have to follow when implementing operator overloads. ng type. What designates this as an operator is the use of the keyword operator, followed by the + symbol. The parameter types are both of the enclosing type, Matrix. The implementation of the operator overload creates a new instance of the return type and performs a matrix add.

Operator Rules

            C# enforces certain rules when you overload operators. One rule is that you must implement the operator overload in the type that will use it. This is sensible because it makes the type self-contained.
            Another rule is that you must implement matching operators. For example, if you overload ==, you must also implement !=. The same goes for <= and >=.
            When you implement an operator, its compound operator works also. For example, since the + operator for the Matrix type was implemented, you can also use the += operator on Matrix types.