Methods

 
Chapter 2 - C# Basics
bySimon Robinsonet al.
Wrox Press 2002
  

Methods

Now that we have seen how to code up the flow of control within a method and how to define classes that can contain methods, we shall have a look at how to define methods in C#.

In VB and C, and even in C++, we could of course define global functions that were not associated with a particular class. This is not the case in C#. As noted earlier, in C# every function must be associated with a class or struct.

Note that official C# terminology does in fact make a distinction between functions and methods. In this terminology, the term 'function member' includes not only methods, but also other non-data members of a class or struct. This includes indexers, operators, constructors, destructors, and also perhaps somewhat surprisingly properties. These are contrasted with data members : fields, constants, and events. In this chapter, we will confine ourselves to looking at methods.

Declaring Methods

The syntax for defining a method in C# is just what you'd expect from a C-style language, and is virtually identical to the syntax in C++ and Java. The only difference is that, in C#, each method is separately declared as public or private . It is not possible to use public: blocks to group several method definitions. Also, all C# methods are declared and defined in the class definition. There is no facility in C# to separate the method implementation as in C++.

In C#, the definition of a method consists of any method modifiers (such as the method's accessibility), the type of the return value, followed by the name of the method, followed by a list of input arguments enclosed in parentheses, followed by the body of the method enclosed in curly braces:

 [  modifiers  ]  return   _   type MethodName  ([  parameters  ]) {    // Method body } 

Each parameter consists of the name of the type of the parameter, and the name by which it can be referenced in the body of the method. Also, if the method returns a value, a return statement must be used with the return value to indicate the exit point. For example:

   public bool IsSquare(Rectangle rect)     {     return (rect.Height == rect.Width);     }   

This code uses one of the .NET base classes, System.Drawing.Rectangle , which represents a rectangle.

If the method doesn't return anything, we specify a return type of void , as we can't omit the return type altogether, and if it takes no arguments, we still need to include an empty set of parentheses after the method name (as with the Main() method that we met early in the chapter). In this case, including a return statement is optional the method returns automatically when the closing curly brace is reached. You should note that a method can contain as many return statements as required:

   public bool IsPositive(int value)     {     if (value < 0)     return false;     return true;     }   

Invoking Methods

The syntax for invoking a method is exactly the same in C# as it is in C++ and Java, and the only difference between C# and VB is that round brackets must always be used when invoking the method in C# this is actually simpler than VB6 's set of rules whereby brackets were sometimes necessary and at other times not allowed.

The following sample, MathTest , illustrates the syntax for definition of and instantiation of classes, and definition of and invocation of methods. Besides the class that contains the Main() method, it defines a class named MathTest, which contains a couple of methods and a field. We briefly covered parts of this class earlier in the chapter:

   using System;     namespace Wrox.ProCSharp.Basics     {     class MainEntryPoint     {     static void Main()     {     // Try calling some static functions     Console.WriteLine("Pi is " + MathTest.GetPi());     int x = MathTest.GetSquareOf(5);     Console.WriteLine("Square of 5 is " + x);     // Instantiate at MathTest object     MathTest math = new MathTest();   // this is C#'s way of     // instantiating a reference type         // Call non-static methods     math.value = 30;     Console.WriteLine(     "Value field of math variable contains " + math.value);     Console.WriteLine("Square of 30 is " + math.GetSquare());     }     }     // Define a class named MathTest on which we will call a method     class MathTest     {     public int value;     public int GetSquare()     {     return value*value;     }     public static int GetSquareOf(int x)     {     return x*x;     }     public static double GetPi()     {     return 3.14159;     }     }     }   

Running the MathTest sample produces these results:

  csc MathTest.cs  Microsoft (R) Visual C# .NET Compiler version 7.00.9466 for Microsoft (R) .NET Framework version 1.0.3705 Copyright (C) Microsoft Corporation 2001. All rights reserved. MathTest Pi is 3.14159 Square of 5 is 25 Value field of math variable contains 30 Square of 30 is 900 

As you can see from the code, our MathTest class contains a field that contains a number, as well as a method to find the square of this number. It also contains two static methods, to return the value of pi, and to find the square of the number passed in as a parameter.

There are some features of this class that are not really good examples of C# program design. For example, GetPi() would usually be implemented as a const field, but good design would mean using concepts that we are not going to introduce until Chapter 3.

Most of this syntax should be familiar to C++ and Java developers. If your background is in VB, then just think of the MathTest class as being like a VB class module that implements fields and methods. There are a couple of points to watch out for though, whatever your language.

For VB people, the idea of static methods will be new (and static in C# does not mean the same thing as Static in VB). As mentioned earlier, a static method (or field) is associated with the class definition as a whole, not with any particular instance of that class. This means they are called by specifying the class name, not the variable name. The difference between the GetSquare() and the GetSquareOf() methods in MathSample is that GetSquare() returns the square of the value field in the class, while GetSquareOf() returns the square of the value supplied as a parameter. Since GetSquareOf() does not use any data associated with any particular instance of the class, we can define it as static .

C++ programmers should note that C# class definitions should not be followed by semi-colons, and static members must always be called by specifying the class name. C++ allowed you alternatively to specify a variable name (in C++, the expression math.GetPi() would be allowed). This is not permitted in C#. Also, notice that the C# compiler is happy for the MathTest class to be referenced in the Main() function before the actual definition of the MathTest class. Unlike in C++, the C# compiler is not worried about the order in which classes are defined, as long as the class is defined somewhere in your sourcecode.

Finally, just as in C++, Java, and VB, you don't need to specify the object name if you are calling a method from within the same class. Specifying the name of the method or field is sufficient.

Passing Parameters to Methods

Arguments can in general be passed into methods by reference, or by value. A variable that is passed by reference to a method is affected by any changes that the called method makes to it, while a variable that is passed by value to a method is not changed by any changes made within the body of the method. This is because methods refer to the original variables when those variables are passed by reference, but only to copies of those variables when they are passed by value. For complex data types, passing by reference is more efficient because of the large amount of data that must be copied when passing by value.

In C#, all parameters are passed by value unless we specifically say otherwise . This is the same behavior as in C++, but the opposite to VB. However, the data type of the parameter also determines the effective behavior of any parameters passed to a method. Since reference types only hold a reference to an object, they will still only pass this reference into the method. Value types, in contrast, hold the actual data, so a copy of the data itself will be passed into the method. An int , for instance, is passed by value to a method, and any changes that the method makes to the value of that int do not change the value of the original int object. Conversely, if an array or any other reference type, such as a class, is passed into a method, and the method changes a value in that array, the new value is reflected in the original array object.

Here is an example, ParameterTest.cs , that demonstrates this:

 using System; namespace Wrox.ProCSharp.Basics {   class ParameterTest     {     static void SomeFunction(int[] ints, int i)     {     ints[0] = 100;     i = 100;     }         public static int Main()     {     int i = 0;     int[] ints = { 0, 1, 2, 4, 8 };     // Display the original values     Console.WriteLine("i = " + i);     Console.WriteLine("ints[0] = " + ints[0]);     Console.WriteLine("Calling SomeFunction...");     // After this method returns, ints will be changed,     // but i will not     SomeFunction(ints, i);     Console.WriteLine("i = " + i);     Console.WriteLine("ints[0] = " + ints[0]);     return 0;     }     }   } 

The output of this is:

  csc ParameterTest.cs  Microsoft (R) Visual C# .NET Compiler version 7.00.9466 for Microsoft (R) .NET Framework version 1.0.3705 Copyright (C) Microsoft Corporation 2001. All rights reserved. ParameterTest i = 0 ints[0] = 0 Calling SomeFunction... i = 0 ints[0] = 100 

Notice how the value of i remains unchanged, but the value we changed in ints is also changed in the original array.

One point to remember is that strings are immutable (if we alter a string's value, we create an entirely new string), so strings don't display the typical reference-type behavior. Any changes made to a string within a method call won't affect the original string.

The behavior described above is the default. We can, however, force value parameters to be passed by reference. To do so, we use the ref keyword. If a parameter is passed to a method, and if the input argument for that method is prefixed with the ref keyword, then any changes that the method makes to the variable will affect the value of the original object:

   static void SomeFunction(int[] ints, ref int i)   {    ints[0] = 100;    i = 100; } 

We will also need to add the ref keyword when we invoke the method:

   SomeFunction(ints, ref i);   

Adding the ref keyword in C# serves the same purpose as using the & syntax in C++ to specify passing by reference. However, C# makes the behavior more explicit (thus hopefully preventing bugs ) by requiring the use of the ref keyword when invoking the method.

Finally, it is also important to understand that C# continues to apply initialization requirements to parameters passed to methods. Any variable must be initialized before it is passed into a method, whether it is passed in by value or reference.

The out Keyword

In C-style languages, it is common for functions to be able to output more than one value from a single routine. This is accomplished using output parameters , in other words, by assigning the output values to variables that have been passed to the method by reference. Often, the starting values of the variables that are passed by reference are unimportant. Those values will be overwritten by the function, which may never even look at them.

It would be convenient if we could use the same convention in C#, but if you remember, C# requires that variables be initialized with a starting value before they are referenced. Although we could initialize our input variables with meaningless values before passing them into a function that will fill them with real, meaningful ones, this practice seems at best needless, and at worst, confusing. However, there is a way to short-circuit the C# compiler's insistence on initial values for input arguments.

This is achieved with the out keyword. When a method's input argument is prefixed with the out keyword, that method can be passed a variable that has not been initialized with a starting value. The variable is passed by reference, so any changes that the method makes to the variable will persist when control returns from the called method. Again, we also need to use the out keyword when we call the method, as well as when we define it:

   static void SomeFunction(out int i)     {     i = 100;     }     public static int Main()     {     int i; // note how i is declared but not initialized     SomeFunction(out i);     Console.WriteLine(i);     return 0;     }   

If the out parameter isn't assigned a value within the body of the function, the method won't compile.

The out keyword is an example of something new in C# that has no analogy in either VB or C++, and which has been introduced to make C# more secure against bugs.

  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net