Team LiB   Previous Section   Next Section

8.7 Static and Instance Members

The fields, properties, and methods of a class can be either instance members or static members. Instance members are associated with instances of a type, while static members are associated with the class and not with any particular instance. Methods are instance methods unless you explicitly mark them with the keyword static.

The vast majority of methods will be instance methods. The semantics of an instance method are that you are taking an action on a specific object. From time to time, however, it is convenient to be able to invoke a method without having an instance of the class, and for that you will use a static method.

You access a static member through the name of the class in which it is declared. For example, suppose you have a class named Button and have instantiated objects of that class named btnUpdate and btnDelete.

Suppose that the Button class has an instance method Draw() and a static method GetButtonCount(). The job of Draw() is to draw the current button, and the job of GetButtonCount is to return the number of buttons currently visible on the form.

You access an instance method through an instance of the class — that is, through an object:

btnUpdate.SomeMethod();

You access a static method through the class name, not through an instance:

Button.GetButtonCount();

8.7.1 Invoking Static Methods

Static methods are said to operate on the class, rather than on an instance of the class. They do not have a this reference, as there is no instance to point to.

Static methods cannot directly access nonstatic members. You will remember that Main() is marked static. For Main() to call a nonstatic method of any class, including its own class, it must instantiate an object.

For the next example, use Visual Studio .NET to create a new console application named StaticTester. VS.NET creates a namespace StaticTester and a class named Class1. Rename Class1 to Tester. Get rid of all the comments and the attribute [STATThread] that Visual Studio .NET puts above Main(). Delete the args parameter to Main(). When you are done, your source code should look like this:

using System;
namespace StaticTester
{
   class Tester
   {
      static void Main()
      {
      }
   }
}

That is a good starting point. Until now, you've always done all the work of the program right in the Main() method, but now you'll create an instance method, Run(). The work of the program will now be done in the Run() method, rather than in the Main() method.

Within the class, but not within the Main() method, declare a new instance method named Run(). When you declare a method you write the accessor (public), followed by the return type, the identifier, and then parentheses.

public void Run()

The parentheses will hold parameters, but Run() won't have any parameters, so you can just leave the parentheses empty. Create braces for the method, and within the braces, just print Hello World to the console.

public void Run()
{
    Console.WriteLine("Hello world");
}

Run() is an instance method. Main() is a static method and cannot invoke Run() directly. You will therefore create an instance of the Tester class and call Run() on that instance:

Tester t = new Tester();

When you type the keyword new, IntelliSense tries to help you with the class name. You'll find that Tester is in the list; it is a legitimate class like any other.

On the next line, invoke Run() on your Tester object t. When you type t followed by the dot operator, IntelliSense presents all the public methods of the Tester class, as shown in Figure 8-5.

Figure 8-5. IntelliSense
figs/lcs_0805.gif

Notice that the Tester class has a number of methods you did not create (e.g., Equals, Finalize, etc.). Every class in C# is an object, and these methods are part of the Object class. This is covered in Chapter 11.

When your program is complete it looks like Example 8-6.

Example 8-6. Instance methods
using System;
namespace StaticTester
{
    // create the class
   class Tester
   {
      // Run is an instance method
      public void Run()
      {
          Console.WriteLine("Hello world");
      }

      // Main is static
      static void Main()
      {
          // create an instance
          Tester t = new Tester();

          // invoke the instance method
          t.Run();
      }
   }
}
Output:
Hello world

This is the model you'll use from now on in console applications. The Main() method will be limited to instantiating an object and then invoking the Run method. You can modify Visual Studio .NET to set this up for you, as described in the following section.

8.7.2 Customizing VS.NET to Create a Run Method

You can customize Visual Studio .NET to create the Run() method for you each time you create a console application. Before doing this, make sure you have a current backup of your entire installation. Then follow these steps to create a template for console applications for use with this book:

  1. Navigate to the Templates directory. Typically this path is Program Files\ Microsoft Visual Studio .NET\ VC#\ VC#Wizards\ CSharpConsoleWiz\ Templates\ 1033.

  2. Make a copy of file1.cs.

  3. Open file1.cs to edit using Notepad or similar text editor.

  4. Remove whatever comments you no longer want in your console applications.

  5. Change the class name from [! output SAFE_CLASS NAME] to Tester.

  6. Remove the parameters to Main() and the attribute above it.

  7. Add the Run() method.

  8. Instantiate Tester and invoke Run from within Main(). When you are done, your file1.cs file should look like this:

    // Customized for Getting Started With C#
    // Copyright (c) 2002 Liberty Associates, Inc.
    
    using System;
    
    namespace [!output SAFE_NAMESPACE_NAME]
    {
       class Tester
       {
          public void Run()
          {
          }
    
          static void Main()
          {
             Tester t = new Tester();
             t.Run();
          }
       }
    }
  9. Save the file and start up Visual Studio .NET. Create a new console application to test that your code worked.

If at any time you decide you'd like to revert to the original template, simply restore the backup copy of file1.cs that you made earlier.

8.7.3 Using Static Fields

A common use of static member variables, or fields, is to keep track of the number of instances/objects that currently exist for your class. In the next example, you create a Cat class. The Cat class might be used in a pet-store simulation.

For this example, the Cat class has been stripped to its absolute essentials. The complete listing is shown in Example 8-7. Analysis follows.

Example 8-7. Static fields
using System;

namespace Test_Console_App_3
{

    // declare a Cat class
    // stripped down
    public class Cat
    {
        // a private static member to keep
        // track of how many Cat objects have
        // been created
        private static int instances = 0;
        private int weight;
        private String name;

        // cat constructor
        // increments the count of Cats
        public Cat(String name, int weight) 
        {
            instances++;  
            this.name = name;
            this.weight = weight;
        }

        // Static method to retrieve
        // the current number of Cats
        public static void HowManyCats()
        {
            Console.WriteLine("{0} cats adopted",
                instances);
        }
        public void TellWeight()
        {
            Console.WriteLine("{0} is {1} pounds", 
                name, weight);
        }
    }

   class Tester
   {
      public void Run()
      {
          Cat.HowManyCats();
          Cat frisky = new Cat("Frisky", 5);
          frisky.TellWeight();
          Cat.HowManyCats();
          Cat whiskers = new Cat("Whisky", 7);
          whiskers.TellWeight();
          Cat.HowManyCats();
      }

      static void Main()
      {
         Tester t = new Tester();
         t.Run();
      }
   }
}
Output:
0 cats adopted
Frisky is 5 pounds
1 cats adopted
Whisky is 7 pounds
2 cats adopted

The Cat class begins by defining a static member variable, instances, that is initialized to zero. This static member field will keep track of the number of Cat objects created. Each time the constructor runs (creating a new object), the instances field is incremented.

The Cat class also defines two instance fields: name and weight. These track the name and weight of each individual Cat object.

The Cat class defines two methods: HowManyCats() and TellWeight(). HowManyCats() is static. The number of Cats is not an attribute of any given Cat; it is an attribute of the entire class. TellWeight() is an instance method. The name and weight of each cat is per instance (i.e., each Cat has his own name and weight).

In the Tester class, Main() creates an instance of Tester, which then calls Run(). The Run() method accesses the static HowManyCats() method directly, through the class:

Cat.HowManyCats();

Run() then creates an instance of Cat and accesses the instance method, TellWeight(), through the instance of Cat:

Cat frisky = new Cat()
frisky.TellWeight();

Each time a new Cat is created, HowManyCats() reports the increase.

    Team LiB   Previous Section   Next Section