In “Anatomy of a C# class,” I began a discussion of C# classes by describing the access control and bases. I also mentioned how classes are packaged into self-describing assemblies. In this article, I’ll continue the discussion with a look at class members—specifically, constants, fields, and methods.

Constants and fields
A constant provides a way to define a named value that is strongly typed. A field defines a variable within the class. The access control modifiers plus the new andstatic modifiers may be applied to both constants and fields. A constant or field may be one of the built-in types or a user-defined type.

The value of the constant is specified in the declaration and can’t be reassigned. For example:
 
class A
{
  public const int k = 1000;
  public const string me = “Paul”;
}

The value must be a constant expression that the C# compiler can evaluate at compile time. This is easy enough for value types such as integers, but what about reference types? Aside from strings, reference types are null until they’re assigned an object created with the new operator. The problem is that you can’t use the new operator in a constant expression. This doesn’t seem too useful—how many names for null do you need anyhow? Fields offer a solution.

The value for a field may also be specified in the declaration. This is a nice convenience and it makes it easy to ensure that a field gets a consistent initial value when a class defines multiple constructors. Unlike constants, the value of a field isn’t calculated at compile time. Field values are determined a run-time, either when the class is loaded, in the case of a static field, or when an object is created, in the case of an instance field.

Constants are implicitly static, though the static modifier is not permitted. Declaring a static field nearly achieves the functionality of a constant. The final touch is to apply the readonly attribute to the field, and voila—you have a member with constant-like properties that can hold non-null references. The remaining difference is that unlike a constant, a readonly field may be reassigned within a constructor to set a constructor-specific value. A readonly field is immutable outside a constructor. The example below shows the different kinds of fields.
 
using System;
 
public class F
{
  public int a = 10;                 // Instance field.
  public readonly int b = 20;        // Immutable instance field.
  public static int c = 30;          // Static field.
  public static readonly int d = 40; // Immutable static field.
  public static readonly object obj = new object();
 
  static F()    // Static constructor for F.
  {
    F.d = 50;
  }
 
  public static void Main()
  {
    F f = new F();
 
    Console.WriteLine
      (
      “a = {0}, b = {1}, F.c = {2}, F.d = {3}, F.obj = {4}”,
      f.a, f.b, F.c, F.d, F.obj
      );
  }
}
 
Output:
a = 10, b = 20, F.c = 30, F.d = 50, F.obj = System.Object

Methods
Methods are primary means to define behavior for a class. As with the class itself, the access control modifiers and the new, abstract, and sealed modifiers may be applied to a method. The virtual, override, and extern attributes may be applied as well. A class may declare methods that either require or allow customization by a derived class. An abstract method provides no implementation and requires a nonabstract derived class to provide one. A virtual method provides an implementation but allows a derived class to provide an alternative. In both cases, a derived class is said to override the base class method.
 
using System;
 
public abstract class A
{
  public abstract void X();
}
 
public abstract class B : A
{
  public virtual void Y()
  {
    Console.WriteLine( “B.Y” );
  }
}
 
public class C : B
{
  // Implement abstract method X.
  public override void X()
  {
    Console.WriteLine( “C.X” );
  }
 
  // Override and seal virtual method Y.
  public override sealed void Y()
  {
    Console.WriteLine( “C.Y” );
  }
 
  // Access the base implementation of method Y.
  public void Z()
  {
    base.Y();
  }
 
  public static void Main()
  {
    C c = new C();
    c.X();
    c.Y();
    c.Z();
  }
}
 
Output:
C.X
C.Y
B.Y

In the example above, abstract class A defines abstract method X. Abstract class B derives from A and defines virtual method Y. As an abstract class, B isn’t required to implement method X. Nonabstract class C derives from B and overrides methods X and Y. The override modifier clearly indicates the intent for C’s X and Y methods. The new modifier could be applied to C.Y instead of the override modifier to hide B.Y rather than override it. C.Y also specifies the sealed modifier. This prevents a class derived from C from overriding C.Y. However, a derived class could override C.X. The overridesealed combination doesn’t compile with the Beta 1 C# compiler. You’ll need Beta 2 to use this combination. Finally, a derived class can access virtual methods in its base class, even if it has overridden them, using the base reserved word as shown in C.Z.

The extern modifier, in combination with the DllImport attribute, can be applied to a method to specify that the implementation is located in an external DLL. This enables access to existing unmanaged code from C#. In the example below, the Win32 API function GetCurrrentProcessId in kernel32.dll is mapped to a static method of the same name in the class ExternExample.
 
using System;
using System.Runtime.InteropServices;   // for DllImport attribute.
 
public class ExternExample
{
  [DllImport( “kernel32” )]
  static extern int GetCurrentProcessId();
 
  public static void Main()
  {
    Console.WriteLine( “Current process ID: {0}”,
      GetCurrentProcessId() );
  }
}
 
Output:
Current process ID: 1688

Method parameters
A method specifies a return type (or void), a name, and a formal parameter list. The name and the formal parameter list are known as the signature of the method. A class may contain more than one method with the same name as long as each method has a different signature. Under such circumstances, the method name is said to be overloaded.

The formal parameter list can define fixed parameters. These can be followed by a parameter array. Each fixed parameter specifies a type and an identifier. A parameter array specifies the params modifier, an array type, and an identifier. A parameter array allows a method to be called with a variable number of arguments, though they must all be of the same type. In the example below, the PrintWords method is called with 0, 1, and 3 arguments for the parameter array.
 
using System;
 
public class ParamArrayExample
{
 
  public void PrintWords( string title, params string[] words )
  {
    Console.Write( “{0}, length {1}: “, title, words.Length );
 
    foreach ( string word in words )
      Console.Write( ” {0}”, word );
 
    Console.WriteLine();
 
  }
 
  public static void Main()
  {
    ParamArrayExample example = new ParamArrayExample();
 
    example.PrintWords( “zero” );
    example.PrintWords( “one”, “white” );
    example.PrintWords( “three”, “red”, “green”, “blue” );
  }
}
 
Output:
zero, length 0:
one, length 1:  white
three, length 3:  red green blue

By default, a fixed parameter is passed by value. The ref modifier can be specified to pass the parameter by reference. The out modifier can be specified to declare that a fixed parameter is an output parameter. A variable passed as the argument for an output parameter does not require initial assignment before being passed in a method invocation.

Arguments for ref or out parameters require the ref and out modifier in the method invocation. This forces the caller to acknowledge the parameter passing method and allows a reader to understand the passing method without referring to the method definition. In the example below, the AddDelta method adds a specified delta to a target variable and sets output parameters to the initial target value plus twice and three times the delta, respectively.
 
using System;
 
public class ParamModifiersExample
{
  public void AddDelta
          ( int delta, ref int target,  out int x2, out int x3 )
  {
    x2 = target + ( delta * 2 );
    x3 = target + ( delta * 3 );
    target += delta;
  }
 
  public static void Main()
  {
    int Delta = 10;
    int Target = 1;
    int X2;  // No initial assignment.
    int X3;  // No initial assignment.
 
    ParamModifiersExample example = new ParamModifiersExample();
    example.AddDelta( Delta, ref Target, out X2, out X3 );
 
    Console.WriteLine
      (
      “Delta = {0}, Target = {1}, X2 = {2}, X3 = {3}”,
       Delta, Target, X2, X3
      );
  }
}
 
Output:
Delta = 10, Target = 11, X2 = 21, X3 = 31

Conclusion
As we continue to build on our knowledge of C#, you will find that thoughtful use of constants and modifiers such as static can make your programs perform better and will make them easier to read. In this article, we discussed how you can communicate with your method via parameters and how a parameter array can allow you to pass a variable number of parameters. These basics will be valuable to you as you continue down the road to learning C#.