#228 – Object-Oriented Programming in C# Using Classes

Classes are the construct in C# that enable the object-oriented paradigm and make C# an object-oriented language.

Classes are the user-defined types that define a set of related data and the methods that act upon that data.  Each instance of the class (an object) has a set of values for the class’ data, know as the object’s state.

Object-orientation is a powerful paradigm to use when writing software because using classes and objects helps you think about things, rather than algorithms.

For example, we might have a Dog class with data fields like: Name, Age, Breed, LikesBalls, and PersonalityType.  We might have methods in the Dog class like Bark, Sit, and Fetch.  Each instance of a Dog would have different values for these fields and we can call any of the Dog methods on an instance of a dog and it would respond based on the value of these fields.

#227 – Instances of Classes Are Created on the Heap

Because they are reference types, instances of classes are stored on the heap.  When you instantiate (or create) an instance of a class, the actual object is created on the heap and you reference the newly created object with a variable whose type corresponds to the class’ type.  The variable that references the object is stored on the stack.

            // New Person object created on the heap
            // p variable is a stack-based reference to the new object
            Person p = new Person("Cary", "Grant");

Because a class object is created on the heap, you don’t destroy the object explicitly, but it is automatically garbage collected.  The object will be a candidate for garbage collection when there are no longer any variables that reference the object.

            p = new Person("Jimmy", "Stewart");

            // No variable references Cary Grant anymore,
            // so he can be garbage-collected.

#226 – Classes and Objects

A class is a data structure containing data and associated behavior.  You use the class keyword in C# to define a new class.  You use your user-defined class in the same way that you use built-in classes in the .NET Framework.

You can create an instance of a class, also known as an object, using the new keyword.  Each instance of a class has its own copy of the data defined by the class.

Here’s an example, a declaration of a new Person class.

    public class Person
    {
        public string FirstName;
        public string LastName;
        public int Age;

        public string DescribeMe()
        {
            return string.Format("{0} {1} is {2} yrs old.",
                                 FirstName, LastName, Age);
        }
    }

Once the class is defined, we can create new instances of the class, read/write its data and call its methods.

            Person p = new Person();
            p.FirstName = "James";
            p.LastName = "Joyce";
            p.Age = 40;

            string desc = p.DescribeMe();

#225 – Free Static Analysis Using FxCop

Although code analysis can be extremely helpful, it’s available in only the Premium and Ultimate editions of Visual Studio 2010.  If you don’t have access to one of these editions but still want to be able to do some sort of static analysis, you can use the (free) FxCop tool.  FxCop used to be distributed separately, but is now part of the Windows SDK.

FxCop works by doing static analysis of your assemblies and looking for violations of programming and design rules.

Start by downloading and installing the Windows SDK.  After the install, you’ll see FxCop in your Start Menu.

After you start up FxCop, select Project | Add Targets and select the .dll or .exe file that you want to analyze.  Then click the Analyze button.

When analysis completes, you’ll see a list of messages indicating suggested changes.

#224 – One Example of a Problem that Code Analysis Would Catch

As an example of the type of issues that code analysis would catch, consider the following piece of code.

        static void Main()
        {
            Console.WriteLine("Grande schmande, just give me some coffee..");
        }

This code obviously works fine–it just prints out a string.  But if we want to someday localize this program, we’ll want to be reading the string from a resource file, rather than hard-coding it like this.  So when we build the project with code analysis turned on, we see the following warning:

CA1303: Do not pass literals as localized parameters

An externally visible method passes a string literal as a parameter to a constructor or method in the .NET Framework class library, and that string should be localizable.

We see this warning in the Error List window when we build the project.

#223 – Enabling Code Analysis for Your Project

If you’re using Visual Studio 2010, the Ultimate and Premium versions come with a built-in code analysis tool, which allows you to check your code for potential problems.  Code analysis will check your code at the time that you build it, looking for logic errors or common mistakes.

To turn code analysis on at build-time, do the following.  Right-click your project in the Solution Explorer and select Properties.

Click on the Code Analysis tab.

Check the box labeled Enable Code Analysis on Build.

The next time that you build your project, the code analysis tool will check it for the problems covered by the Microsoft Basic Correctness Rules rule set.  Any problems found will be reported as Warnings in the Error List window.

#222 – C# Is Pronounced “C Sharp”

C# is always written as “C#”, using the number sign ‘#’ (ASCII 35).  This is actually pronounced “C Sharp”, interpreting the number sign as a musical sharp symbol when pronouncing the name of the language.  Though pronounced as “sharp”, we never write it using the Unicode symbol for the musical sharp sign.

#221 – When Unboxing, You Must Match the Original Type Exactly

We know that we can do explicit casts when assigning from one type to another and the assignment will work if the value being assigned can be represented by the target type.

            int i = 1964;
            uint ui = (uint)i;  // cast works fine

But when casting as part of an unboxing operation, the target type must match the original type exactly.

            int i = 1964;
            object o = i;    // box

            // Attempted unbox to different type
            // Throws InvalidCastException
            uint ui = (uint)o;

This cast, as part of the unboxing operation, is allowed at compile time, but throws an InvalidCastException at run-time.

#220 – Boxing Can Happen Automatically

Boxing can happen automatically in C#, when you pass a value-typed variable to a method that accepts a System.Object (object).

The Console.WriteLine method is a good example of this.  It accepts a format string as its first parameter and then a series of object parameters, which represent objects to substitute into the format string.  (Specifically, Console.WriteLine calls each object’s ToString method to get its string representation).

We can pass in some reference-typed objects to Console.WriteLine.

            Person p = new Person("Lillian", "Gish");

            Console.WriteLine("The actor known as {0} was a silent film star.", p);

We can also pass value-typed variables into Console.WriteLine.  The value types will be boxed–converted into reference types.  An object will be created on the heap for each value-typed parameter and its value copied to the new object.

            int i = 1964;

            Console.WriteLine("Favorite numbers: {0}, {1}, {2}", 42, 6, i);

#219 – Unboxing a Boxed Object

If you’ve converted a value-typed object to a reference-typed object by boxing it, you can later unbox the object, converting it back to a value type.

Assume that we’ve boxed an integer value as follows:

            int i = 1964;
            object o = i;   // Box i

Now we have a copy of the integer in a new object on the heap.

We can later convert this object back to a value type.  This is known as unboxing.

            int j = (int)o;  // Unbox

When we unbox, we need to explicitly cast the object to the value type.  Because the integer derives from System.Object, we can convert it to an object implicitly, but when converting from an object to an int, we need an explicit cast.

As with boxing, unboxing copies the value of the heap-based object.  It does not delete the object on the heap or free its memory.