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( );
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 2005 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 7-1.
Notice that the Tester
class has a number of methods you did not create (Equals
, Finalize
, and others). 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 7-5.
Example 7-5. 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( ); } } }
The output looks like this:
Hello world
This is the model you’ll use from now on in most console
applications. The Main( )
method
will be limited to instantiating an object and then invoking the
Run( )
method.
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
7-6. An analysis follows the example.
Example 7-6. Static fields
using System; namespace Test { // 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( ); } } }
Here is the 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
(each Cat
has his own name and
weight).
The Main( )
method accesses
the static HowManyCats( )
method
directly, through the class:
Cat.HowManyCats( );
Main( )
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.