Chapter 4. Let Me Say This about this

In This Chapter

  • How to pass an object to a method

  • Class methods versus instance methods

  • Understanding what this is

  • When you don't have this

  • When a class doesn't have a method that you need it to have

This chapter moves from the static methods that Chapter 3 in this minibook emphasizes to the nonstatic methods of a class. Static methods belong to the whole class, and nonstatic methods belong to each instance created from the class. Important differences exist between static and nonstatic class members.

Passing an Object to a Method

You pass object references as arguments to methods in the same way as you pass value-type variables, with one difference: You always pass objects by reference.

The following small program demonstrates how you pass objects — to methods, that is:

Note

// PassObject -- Demonstrate how to pass an object to a method.
using System;
namespace PassObject
{
  public class Student
  {
    public string name;
  }
  public class Program
  {
    public static void Main(string[] args)
    {
      Student student = new Student();
      // Set the name by accessing it directly.
      Console.WriteLine("The first time:");
      student.name = "Madeleine";
OutputName(student);
      // Change the name using a method.
      Console.WriteLine("After being modified:");
      SetName(student, "Willa");
      OutputName(student);
      // Wait for user to acknowledge.
      Console.WriteLine("Press Enter to terminate...");
      Console.Read();
    }
    // OutputName -- Output the student's name.
    public static void OutputName(Student student)
    {
      // Output current student's name.
      Console.WriteLine("Student's name is {0}", student.name);
    }
    // SetName -- Modify the student object's name.
    public static void SetName(Student student, string name)
    {
      student.name = name;
    }
  }
}

The program creates a student object consisting of nothing but a name. The program first sets the name of the student directly and passes the student object to the output method OutputName(). OutputName() displays the name of any Student object it receives.

The program then updates the name of the student by calling SetName(). Because all reference-type objects are passed by reference in C#, the changes made to student are retained in the calling method. When Main() outputs the student object again, the name has changed, as shown in this bit of code:

The first time:
Student's name is Madeleine
After being modified:
Student's name is Willa
Press Enter to terminate...

The SetName() method can change the name within the Student object and make it stick.

Note

You don't use the ref keyword when passing a reference-type object. Yet the effect is that the object's contents can be modified through the reference. However, if SetName() tries to assign a whole new Student object to its Student parameter, it doesn't affect the original Student object outside the method, as this chunk of code shows:

Student student = new Student();
SetName(student, "Pam");
Console.WriteLine(student.name);  // Still "Pam"
...
// A revised SetName():
public static void SetName(Student student, string name)
{
  student = new Student(); // Doesn't replace student outside SetName().
  student.Name = name;
}

Defining Methods

A class is supposed to collect the elements that describe a real-world object or concept. For example, a Vehicle class may contain data elements for maximum velocity, weight, and carrying capacity, for example. However, a Vehicle has active properties — behaviors — as well: the ability to start and stop and the like. These active properties are described by the methods related to that vehicular data. These methods are just as much a part of the Vehicle class as the data elements.

Defining a static method

For example, you could rewrite the program from the previous section in a slightly better way:

Note

// StudentClassWithMethods -- Demonstrate putting methods that
//    operate on a class's data inside the class. A class is
//    responsible for its own data and any operations on it.
using System;
namespace StudentClassWithMethods
{
  // Now the OutputName and SetName methods are members of
  // class Student, not class Program.
  public class Student
  {
    public string name;
    // OutputName -- Output the student's name.
    public static void OutputName(Student student)
    {
      // Output current student's name.
      Console.WriteLine("Student's name is {0}", student.name);
   }
   // SetName -- Modify the student object's name.
   public static void SetName(Student student, string name)
   {
     student.name = name;
   }
  }
  public class Program
  {
    public static void Main(string[] args)
    {
      Student student = new Student();
      // Set the name by accessing it directly.
      Console.WriteLine("The first time:");
      student.name = "Madeleine";
Student.OutputName(student); // Method now belongs to Student.
      // Change the name using a method.
      Console.WriteLine("After being modified:");
      Student.SetName(student, "Willa");
      Student.OutputName(student);
      // Wait for user to acknowledge.
      Console.WriteLine("Press Enter to terminate...");
      Console.Read();
    }
  }
}

Other than its name, this program has only one significant change from the PassObject program in the previous section: I put the OutputName() and SetName() methods in the Student class.

Tip

Rather than say "in" the class, many programmers speak of members "on" the class.

Because of that change, Main() must reference the Student class in the calls to SetName() and OutputName(). The methods are now members of the class Student and not Program, the class in which Main() resides.

This step is small but significant. Placing OutputName() within the class leads to a higher level of reuse: Outside methods that need to display the object can find OutputName() right there as part of the class. It doesn't have to be written separately by each program using the Student class.

This solution is also better on a philosophical level. Class Program shouldn't need to worry about how to initialize the name of a Student object nor about how to output important material. The Student class should contain that information. Objects are responsible for themselves.

Tip

In fact, Main() shouldn't initialize the name to Madeleine in the first place. It should call SetName() instead.

From within Student, one member method can invoke another without explicitly applying the class name. SetName() could invoke OutputName() without needing to reference the class name. If you leave off the class name, C# assumes that the method being accessed is in or on the same class.

Defining an instance method

Although OutputName() and SetName() are static methods, they could as easily be nonstatic, or instance, methods.

Note

All static members of a class are class members, and all nonstatic members are instance members. This includes methods.

The nonstatic data members of an object — an instance of a class — are accessed with the object and not with the class. Thus, you may say

Student student = new Student(); // Create an instance of Student.
student.name = "Madeleine";      // Access the member via the instance.

C# enables you to invoke nonstatic member methods in the same way:

student.SetName("Madeleine");

The following example demonstrates this technique:

Note

// InvokeMethod -- Invoke a member method through the object.
using System;
namespace InvokeMethod
{
  class Student
  {
    // The name information to describe a student
    public string firstName;
    public string lastName;
    // SetName -- Save name information. (Nonstatic.)
    public void SetName(string fName, string lName)
    {
      firstName = fName;
      lastName  = lName;
    }
    // ToNameString -- Convert the student object into a
    //    string for display. (Nonstatic.)
    public string ToNameString()
    {
      string s = firstName + " " + lastName;
      return s;
    }
  }
  public class Program
  {
    public static void Main()
    {
      Student student = new Student();
      student.SetName("Stephen", "Davis"); // Call instance method.
      Console.WriteLine("Student's name is "
                       + student.ToNameString());
      // Wait for user to acknowledge.
      Console.WriteLine("Press Enter to terminate...");
      Console.Read();
    }
  }
}

The output from this program is this simple line:

Student's name is Stephen Davis

Other than having a much shorter name, this program is quite similar to the earlier StudentClassWithMethods program. This version uses nonstatic methods to manipulate both a first and last name.

The program begins by creating a new Student object, student. The program then invokes the SetName() method, which stores the two strings "Stephen" and "Davis" into the data members firstName and lastName. Finally, the program calls the member method ToNameString(), which returns the name of the student by concatenating the two strings.

Look again at the SetName() method, which updates the first and last name fields in the Student object. To see which object SetName() modifies, consider this example:

Student christa = new Student();   // Here's one student.
Student sarah = new Student();     // And here's a completely different one.
christa.SetName("Christa", "Smith");
sarah.SetName("Sarah", "Jones");

The first call to SetName() updates the first and last name of the christa object. The second call updates the sarah object.

Tip

Thus, C# programmers say that a method operates on the current object. In the first call, the current object is christa; in the second, it's sarah.

Expanding a method's full name

A subtle but important problem exists with my description of method names. To see the problem, consider this sample code snippet:

public class Person
{
  public void Address()
  {
    Console.WriteLine("Hi");
  }
}
public class Letter
{
  string address;
  // Store the address.
  public void Address(string newAddress)
  {
    address = newAddress;
  }
}

Any subsequent discussion of the Address() method is now ambiguous. The Address() method within Person has nothing to do with the Address() method in Letter. If my programmer friend tells me to access the Address() method, which Address() does he mean?

The problem lies not with the methods themselves, but, rather, with my description. In fact, no Address() method exists as an independent entity — only a Person.Address() and a Letter.Address() method. Attaching the class name to the beginning of the method name clearly indicates which method is intended.

This description is quite similar to people's names. Within my family, I am known as Stephen. (Actually, within my family, I am known by my middle name, but you get the point.) No other Stephens are within my family (at least not within my close family). However, two other Stephens work where I do.

If I'm at lunch with coworkers and the other two Stephens aren't present, the name Stephen clearly refers to me. Back in the trenches (or cubicles), shouting "Stephen" is ambiguous because it can refer to any one of us. In that context, you need to yell out "Stephen Davis" as opposed to "Stephen Williams" or "Stephen Leija."

Thus, you can consider Address() to be the first name or nickname of a method, with its class as the family name.

Accessing the Current Object

Consider the following Student.SetName() method:

class Student
{
  // The name information to describe a student
  public string firstName;
  public string lastName;
  // SetName -- Save name information.
  public void SetName(string firstName, string lastName)
  {
    firstName = firstName;
    lastName  = lastName;
  }
}
public class Program
{
  public static void Main()
  {
    Student student1 = new Student();
    student1.SetName("Joseph", "Smith");
    Student student2 = new Student();
    student2.SetName("John", "Davis");
  }
}

The method Main() uses the SetName() method to update first student1 and then student2. But you don't see a reference to either Student object within SetName() itself. In fact, no reference to a Student object exists. A method is said to operate on "the current object." How does a method know which one is the current object? Will the real current object please stand up?

The answer is simple. The current object is passed as an implicit argument in the call to a method — for example:

student1.SetName("Joseph", "Smith");

This call is equivalent to the following:

Student.SetName(student1, "Joseph", "Smith"); // Equivalent call,
                                      // (but this won't build properly).

I'm not saying that you can invoke SetName() in two different ways; just that the two calls are semantically equivalent. The object identifying the current object — the hidden first argument — is passed to the method, just like other arguments. Leave that task to the compiler.

Passing an object implicitly is easy to swallow, but what about a reference from one method to another? The following code snippet illustrates calling one method from another:

public class Student
{
  public string firstName;
  public string lastName;
  public void SetName(string firstName, string lastName)
  {
    SetFirstName(firstName);
    SetLastName(lastName);
  }
  public void SetFirstName(string name)
  {
    firstName = name;
  }
  public void SetLastName(string name)
  {
    lastName = name;
  }
}

No object appears in the call to SetFirstName(). The current object continues to be passed along silently from one method call to the next. An access to any member from within an object method is assumed to be with respect to the current object. The upshot is that a method "knows" which object it belongs to. "Current object" (or "current instance") means something like "me."

What is the this keyword?

Unlike most arguments, the current object doesn't appear in the method argument list, so it isn't assigned a name by the programmer. Instead, C# assigns this object the less-than-imaginative name this, useful in the few situations where you need to refer directly to the current object.

Warning

The C# keyword this cannot be used for any other purpose, at least not without the express written permission of the National Football League.

Thus you could write the previous example this way:

public class Student
{
  public string firstName;
  public string lastName;
  public void SetName(string firstName, string lastName)
  {
    // Explicitly reference the "current object" referenced by this.
    this.SetFirstName(firstName);
    this.SetLastName(lastName);
  }
  public void SetFirstName(string name)
  {
    this.firstName = name;
  }
  public void SetLastName(string name)
  {
    this.lastName = name;
  }
}

Notice the explicit addition of the keyword this. Adding it to the member references doesn't add anything because this is assumed. However, when Main() makes the following call, this references student1 throughout SetName() and any other method it may call:

student1.SetName("John", "Smith");

When is this explicit?

You don't normally need to refer to this explicitly because it is understood where necessary by the compiler. However, two common cases require this. You may need it when initializing data members, as in this example:

class Person
{
  public string name;  // This is this.name below.
  public int id;       // And this is this.id below.
  public void Init(string name, int id)  // These are method arguments.
  {
    this.name = name;  // Argument names same as data member names
    this.id = id;
  }
}

The arguments to the Init() method are named name and id, which match the names of the corresponding data members. The method is then easy to read because you know immediately which argument is stored where. The only problem is that the name name in the argument list obscures the name of the data member. The compiler complains about it.

Note

The addition of this clarifies which name is intended. Within Init(), the name name refers to the method argument, but this.name refers to the data member.

You also need this when storing the current object for use later or by some other method. Consider this program example ReferencingThis Explicitly:

Note

// ReferencingThisExplicitly -- Demonstrates how to explicitly use
//    the reference to 'this'.
using System;
namespace ReferencingThisExplicitly
{
  public class Program
  {
    public static void Main(string[] strings)
    {
      // Create a student.
      Student student = new Student();
      student.Init("Stephen Davis", 1234);
      // Now enroll the student in a course.
      Console.WriteLine
             ("Enrolling Stephen Davis in Biology 101");
      student.Enroll("Biology 101");
      // Display student course.
      Console.WriteLine("Resulting student record:");
      student.DisplayCourse();
      // Wait for user to acknowledge the results.
      Console.WriteLine("Press Enter to terminate...");
      Console.Read();
    }
  }
  // Student -- The class for university students.
  public class Student
  {
    // All students have a name and an id.
    public string _name;
    public int _id;
    // The course in which the student is enrolled
    CourseInstance _courseInstance;
    // Init -- Initialize the student object.
    public void Init(string name, int id)
    {
      this._name = name;
      this._id = id;
      _courseInstance = null;
    }
    // Enroll -- Enroll the current student in a course.
    public void Enroll(string courseID)
    {
      _courseInstance = new CourseInstance();
      _courseInstance.Init(this, courseID);    // Here's the explicit reference.
    }
    // Display the name of the student and the course.
    public void DisplayCourse()
    {
Console.WriteLine(_name);
      _courseInstance.Display();
    }
  }
  // CourseInstance -- A combination of a student with
  //    a university course.
  public class CourseInstance
  {
    public Student _student;
    public string _courseID;
    // Init -- Tie the student to the course.
    public void Init(Student student, string courseID)
    {
      this._student = student;
      this._courseID = courseID;
    }
    // Display -- Output the name of the course.
    public void Display()
    {
      Console.WriteLine(_courseID);
    }
  }
}

This program is fairly mundane. The Student object has room for a name, an ID, and a single instance of a university course (not an industrious student). Main() creates the student instance and then invokes Init() to initialize the instance. At this point, the _courseInstance reference is set to null because the student isn't yet enrolled in a class.

The Enroll() method enrolls the student by initializing _courseInstance with a new object. However, the CourseInstance.Init() method takes an instance of Student as its first argument along with the course ID as the second argument. Which Student should you pass? Clearly, you need to pass the current Student — the Student referred to by this. (Thus you can say that Enroll() enrolls this student in the CourseInstance.)

Tip

Some programmers (and that includes me) like to differentiate data members from other variables more clearly by prefixing an underscore to the name of each data member, like this: _name. You see me adopt this convention most of the time, but of course, it's only a convention, and you may do as you like. If you use the convention, you don't need to preface the item with this, as in this._id. It's completely unambiguous with just the underscore prefix.

What happens when you don't have this?

Mixing class (static) methods and instance (nonstatic) methods is like mixing sheepmen and ranchers. Fortunately, C# gives you some ways around the problems between the two. To see the problem, consider this program snippet MixingStaticAndInstanceMethods:

Note

// MixingStaticAndInstanceMethods -- Mixing class (static) methods
//    and instance (nonstatic) methods can cause problems.
using System;
namespace MixingStaticAndInstanceMethods
{
  public class Student
  {
    public string _firstName;
    public string _lastName;
    // InitStudent -- Initialize the student object.
    public void InitStudent(string firstName, string lastName)
    {
      _firstName = firstName;
      _lastName = lastName;
    }
    // OutputBanner (static) -- Output the introduction.
    public static void OutputBanner()
    {
      Console.WriteLine("Aren't we clever:");
      // Console.WriteLine(? what student do we use ?); ß The problem!
    }
    // OutputBannerAndName (nonstatic) -- Output intro.
    public void OutputBannerAndName()
    {
      // The class Student is implied but no this
      // object is passed to the static method.
      OutputBanner();
      // The current Student object is passed explicitly.
      OutputName(this);
    }
    // OutputName -- Output the student's name.
    public static void OutputName(Student student)
    {
      // Here, the Student object is referenced explicitly.
      Console.WriteLine("Student's name is {0}",
                        student.ToNameString());
    }
    // ToNameString -- Fetch the student's name.
    public string ToNameString()
    {
      // Here, the current object is implicit --
      // this could have been written:
      // return this._firstName + " " + this._lastName;
      return _firstName + " " + _lastName;
    }
  }
  public class Program
  {
    public static void Main(string[] args)
    {
      Student student = new Student();
      student.InitStudent("Madeleine", "Cather");
       // Output the banner and name statically.
      Student.OutputBanner();
      Student.OutputName(student);
      Console.WriteLine();
// Output the banner and name again using instance.
      student.OutputBannerAndName();
      // Wait for user to acknowledge.
      Console.WriteLine("Press Enter to terminate...");
      Console.Read();
    }
  }
}

Start at the bottom of the program with Main() so that you can better see the problems. The program begins by creating a Student object and initializing its name. The simpleton program now wants to do nothing more than output the name preceded by a short message and banner.

Main() first outputs the banner and message using the class or static method approach. The program invokes the OutputBanner() method for the banner line and the OutputName() method to output the message and the student name. The method OutputBanner() outputs a simple message to the console. Main() passes the student object as an argument to OutputName() so that it can display the student's name.

Next, Main() uses the instance method approach to outputting the banner and message by calling student.OutputBannerAndName().

OutputBannerAndName() first invokes the static method OutputBanner(). The class Student is assumed. No object is passed because the static OutputBanner doesn't need one. Next, OutputBannerAndName() calls the OutputName() method. OutputName() is also a static method, but it takes a Student object as its argument. OutputBannerAndName() passes this for that argument.

A more interesting case is the call to ToNameString() from within OutputName(). OutputName() is declared static and therefore has no this. It has an explicit Student object, which it uses to make the call.

The OutputBanner() method would probably like to call ToName String() as well; however, it has no Student object to use. It has no this reference because it's a static method and wasn't passed an object explicitly. Note the first boldfaced line in the sample code: The static method cannot call the instance method.

Note

A static method cannot call a nonstatic method without explicitly providing an object. No object, no call. In general, static methods cannot access any nonstatic items in the class. But nonstatic (instance) methods can access static as well as instance items: static data members and static methods.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.53.168