Theme index -- Keyboard shortcut: 'u'  Previous theme in this lecture -- Keyboard shortcut: 'p'  Next slide in this lecture -- Keyboard shortcut: 'n'Data Access, Properties, and Methods

A complete PDF version of the text book is now available. The PDF version is an almost complete subset of the HTML version (where only a few, long program listings have been removed). See here.

20.  Methods

Methods are the most important kind of operations in C#. Methods are more fundamental than properties and indexers. We would be able to do object-oriented programming without properties and indexers (by implementing all properties and indexers as methods), but not without methods. In Java, for instance, there are methods but no properties and no indexers.

A method is a procedure or a function, which is part of a class. Methods (are supposed to) access (operate on) the data, which are encapsulated by the class. Methods should be devoted to nontrivial operations. Trivial operations that just read or write individual instance variables should in C# be programmed as properties.

We have already in Section 11.9 and Section 11.11 studied the fundamentals of methods in an object-oriented programming language. In these sections we made the distinction between instance methods and class methods. Stated briefly, instance methods operate on instance variables (and perhaps class variables as well). Class methods (called static methods in C#) can only operate on class variables (static variables in C#).

When we do object-oriented programming we organize most data in instances of classes (objects) and in values of struct types. We only use class related data (in static variables) to a lesser degree. Therefore instance methods are more important to us than class methods. In the rest of this chapter we will therefore focus on instance methods.

This chapter is long because we have to cover a number of different parameter passing modes. If you only need a basic understanding of methods and the most frequently used parameter passing mode - call-by-value parameters - you should read until (and including) Section 20.4 and afterwards proceed to Chapter 21.

20.1 Local variables in methods20.7 Output Parameters
20.2 Parameters20.8 Use of ref and out parameters in OOP
20.3 Value Parameters20.9 Parameter Arrays
20.4 Passing references as value parameters20.10 Extension Methods
20.5 Passing structs as value parameters20.11 Methods versus Properties versus Indexers
20.6 Reference Parameters
 

20.1.  Local variables in methods
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Local variables of a method are declared in the statements part of the block relative to Syntax 20.1. Local variables are short lived; They only exists during the activation of the method.


modifiers return-type method-name(formal-parameter-list){
  statements
}
Syntax 20.1    The syntax of a method in C#

You should notice the difference between local variables and parameters, which we discuss below in Section 20.2. Parameters are passed and initialized when the method is activated. The initial value comes from an actual parameter. Local variables are introduced in the statements part (the body) of the method, and as explained below they may - or may not - be initialized explicitly.

You should also notice the difference between local variables and instance variables of a class. A local variable only exists in a single call of the method. An instance variable exists during the lifetime of an object.

Local variables

  • May be declared anywhere in the block

    • Not necessarily in the initial part of the block

  • Can be initialized by an initializer

  • Can alternatively be declared without an initializer

    • No default value is assigned to a local variable

      • Different from instance and class variables which are given default values

    • The compiler will complain if a local variable is referred without prior assignment

In the program below we contrast instance variables of the class InitDemo with local variables in the method Operation of InitDemo. The purple instance variables are implicitly initialized to their default values. The blue local variables in Operations are not. The program does not compile. In line 18 and 19 the compiler will complain about use of unassigned local variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;

class InitDemo{

  private int intInstanceVar;         
  private bool boolInstanceVar;       

  public void Operation(){
    int intLocalVar;                  
    bool boolLocalVar;                
    
    Console.WriteLine("intInstanceVar: {0}. boolInstanceVar: {1}", 
                      intInstanceVar,
                      boolInstanceVar);

    // Compile time errors:
    Console.WriteLine("intLocalVar: {0}. boolLocalVar: {1}",   
                      intLocalVar,    
                      boolLocalVar);  

  }

  public static void Main(){
    new InitDemo().Operation();
  }

}
Program 20.1    Local variables and instance variables - initialization and default values.

In C#, Java and similar languages there is no such thing as a global variable. This is often a problem for programmers who are used to pass data around via global variables. If you really, really need a global variable in C#, the best option is to use a class (static) variable in one of your top-level classes. In general, however, it is a better alternative to pass data around via parameters (such as parameters to constructors).

 

20.2.  Parameters
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

As a natural counterpart to Syntax 18.1 (of properties) and Syntax 19.1 (of indexers) we have shown the syntactical form of a method in Syntax 20.1. The syntactical characteristic of methods, in contrast to properties and indexers, is the formal parameters in ordinary, soft parentheses: (...). Even a method with no parameters must have an empty pair of parentheses ( ) - in both the method definition and in the method activation. Properties have no formal parameters, and indexers have formal parameters in brackets: [...].

We will now discuss parameter passing in general. The following introduces formal parameters and actual parameters.

Actual parameters occur in a call. Formal parameters occur in the method declaration. In general, actual parameters are expressions which are evaluated to arguments. Depending on the kind of parameters, the arguments are somehow associated with the formal parameters.

C# offers several different parameter passing modes:

  • Value parameters

    • The default parameter passing mode, without use of a modifier

  • Reference parameters

    • Specified with the ref modifier

  • Output parameters

    • Specified with the out modifier

  • Parameter arrays

    • Value parameters specified with the params modifier

Far the majority of the parameters in a C# program are passed by value. Thus, the use of value parameters is the most important parameter passing technique for you to understand. Value parameters are discussed in Section 20.3 - Section 20.5.

Reference and output parameters are closely related to each other, and they are only rarely used in object-oriented C# programs. Output parameter can be seen as a restricted version of reference parameters. Reference parameters stem from variable parameters (var parameters) in Pascal. Reference parameters are discussed in Section 20.6 and out parameters are discussed in Section 20.7.

Parameter arrays cover the idea that a number of actual value parameters (of the same type) are collected into an array. In this way, parameter arrays provide for a more sophisticated correspondence between the arguments and the formal parameters. C# parameter arrays are discussed in Section 20.9.

It is, in general, an issue how a given actual parameter (or argument) is related to a formal parameter. This is called parameter correspondence. With value, ref and out parameter we use positional parameter correspondence. This is simple. The first formal parameter is related to the first actual parameter, the second to the second, etc. With parameter arrays, a number of actual parameters - all remaining actual parameters - correspond to a single formal parameter.

In general, there are other parameter correspondences, most notable keyword parameters, where the name of the formal parameter is used together with the actual parameter. Keyword parameters are not used directly in C#. But as we have seen in Section 18.4 a kind of keyword parameters is used when properties are used for object initialization in the context of the new operator.

In Section 6.9 - Program 6.20 - we have shown a program example that illustrate multiple parameter passing modes in C#. If you wish the ultra short description of parameter passing in C# you can read Section 6.9 instead of Section 20.3 - Section 20.9.

 

20.3.  Value Parameters
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Value parameters are used for input to methods

When the story should told briefly, call-by-value parameters (or just value parameters) are used for input to methods. The output from a method is handled via the value returned from the method - via use of return. This simple version of the story is true for the majority of the methods we write in our classes. But as we will see already in Section 20.4 it is also possible to handle some kinds of output via references to objects passed as value parameters.

Value parameter passing works in the following way:

  • A formal parameter corresponds to a local variable

  • A formal parameter is initialized by the corresponding argument (the value of the actual parameter expression)

    • A copy of the argument is bound to the formal parameter

  • Implicit conversions may take place

  • A formal parameter is assignable, but with no effect outside the method

We have already seen many examples of value parameters. In case you want to review typical examples, please consult the Move method of class Point in Program 11.2, the Withdraw and Deposit methods of class BankAccount in Program 11.8, and the AgeAsOf method in class Person in Program 16.10.

 

20.4.  Passing references as value parameters
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Care must be taken if we pass references as value parameters

Most of the data we deal with in object-oriented C# programs are represented as instances of classes - as objects. This implies that such data are accessed by references. Again and again we pass such references as value parameters to methods. Therefore we must understand - in details - what happens.

Here is the short version of the story. Let us assume that send the message DayDifference to a Date object with another Date object as parameter:

  someDate.DayDifference(otherDate)

Date is a class, and therefore both someDate and otherDate hold references to Date objects. It is possible for the DayDifference method to mutate the Date object referred by otherDate, despite the fact that the parameter of DayDifference is a value parameter. It is not, however, possible for DayDifference to modify value of the variable (actual parameter) otherDate as such.

In the web-version we present the source program behind the example is great details.

In this section we use the types Date and Person, which we encountered in Section 16.5 about privacy leaks. The privacy leak, which we pointed out in Section 16.5, persists in the code that we discuss in this section.

In Program 20.2 we concentrate on the emphasized method DayDifference, which takes a single value parameter called other. The method is intended to calculate the number of days in between the current date and other. When DayDifference is called it is activated on a Date object with another Date object as value parameter. This is a typical situation.

In Program 20.2 we chose to modify the other Date. (This is less typical, we should admit). Concretely, we increment the year of the other Date. Next the "day difference" between the current object and other should be calculated and returned. We do not show how this done.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Date{    
  private int year, month, day;

  public Date(int year, int month, int day){
    this.year = year; this.month = month; this.day = day;
  }

  public int Year{
    get{return year;}
    set{year = value;}
  }

  public int Month{
    get{return month;}
    set{month = value;}
  }

  public int Day{
    get{return day;}
    set{day = value;}
  }

  public int DayDifference(Date other){
    other.year++;   
    return ...;     
  }

  public override string ToString(){
    return string.Format("{0}.{1}.{2}",day, month, year);
  }

}
Program 20.2    The class Date and the method DayDifference with a value parameter.

In Program 20.3 we show an activation of DayDifference in the context of a Person class. The person's dateOfBirth instance variable is passed (by value) to the method DayDifference. The value is a reference. But via this reference we can access the birth date, and the method DayDifference is able to modify this date (because Date instances are mutable - see Section 14.6 ).

Thus, despite the fact that the parameter other of DayDifference is a value parameter (a reference to an object) we can easily in DayDifference mutate the date object. In reality, the parameter of DayDifference corresponds to a call-by-reference parameter in C terminology. We cannot, however, in DayDifference, change the reference contained in the instance variable dateOfBirth in class Person. This is because other of DayDifference in class Date is a value parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Person{

  private string name;
  private Date dateOfBirth, dateOfDeath;

  public Person (string name, Date dateOfBirth){
    this.name = name;
    this.dateOfBirth = dateOfBirth;
    this.dateOfDeath = null;
  }

  public string Name{
    get {return name;}
  }

  public Date DateOfBirth{
    get {return dateOfBirth;}
  }

  public int DayAge(){
    return new Date(2008, 3, 11).DayDifference(dateOfBirth);   
  }                                                            

  public override string ToString(){
    return "Person: " + name + " " + dateOfBirth;
  }

}
Program 20.3    The class Person which calls DayDifference on dateOfBirth.

Below, in Program 20.4 we show a client of Date and Person, and in Listing 20.5 we show the output of the program. The output confirms that birthday of the Person object can be modified, as claimed above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

class Client{

  public static void Main(){

    Person p = new Person("Hanne", new Date(1926, 12, 24));
    Console.WriteLine("{0}", p);

    int age = p.DayAge();  
                           
    Console.WriteLine("{0} of age {1}", p, age);
  }

}
Program 20.4    A client of Person and Date which reveal the consequences.

1
2
Person: Hanne 24.12.1926
Person: Hanne 24.12.1927 of age ...
Listing 20.5    Client program output.

The insight obtained in this section is summarized as follows.

In case a reference is passed as an argument to a value parameter, the referenced object can be modified through the formal parameter


Exercise 5.3. Passing references as ref parameters

In the Date and Person classes of the corresponding slide we pass a reference as a value parameter to method DayDifference in class Date. Be sure to understand this. Read about ref parameters later in this lecture.

Assume in this exercise that the formal parameter other in Date.DayDifference is passed by reference (as a C# ref parameter). Similarly, the actual parameter dateOfBirth to DayDifference should (of course) be passed by reference (using the keyword ref in front of the actual parameter).

What will be the difference caused by this program modification.

Test-drive the program with a suitable client to verify your answer.

Solution


 

20.5.  Passing structs as value parameters
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

This section is parallel to Section 20.4. In this section we pass a struct value (as opposed to an instance of a class) as a value parameter. Our finding is that a struct value (the birthday value of type Date) cannot be mutated from the DayDifference method of struct Date.

If we assume that Date is a struct instead of a class, the expression

  someDate.DayDifference(otherDate)

passes a copy of otherDate to DayDifference. The copy is discarded when we return from DayDifference. If DayDifference mutates the value of its parameter, only the local copy is affected. The value of otherDate is not!

In the web-version we will discuss the same example as in the web-version of Section 20.4.

It is easy to follow Program 20.6 - Listing 20.9 if you have already understood Program 20.2 - Listing 20.5.

Stated briefly, the instance variable dateOfBirth in line 4 of Program 20.7 is a struct. It is copied to DayDifference, because the parameter of DayDifference is a value parameter. The mutation of the formal parameter - other - in line 24 in Program 20.6 DayDifference affects the copy, but not the actual parameter of the call (in line 22 of Program 20.7). Thus, the instance variable dateOfBirth in class Person is not affected. The output of the client program, see Listing 20.9, confirms this observation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public struct Date{                  
  private int year, month, day;

  public Date(int year, int month, int day){
    this.year = year; this.month = month; this.day = day;
  }

  public int Year{
    get{return year;}
    set{year = value;}
  }

  public int Month{
    get{return month;}
    set{month = value;}
  }

  public int Day{
    get{return day;}
    set{day = value;}
  }

  public int DayDifference(Date other){
    other.year++;        
    return ...;          
  }

  public override string ToString(){
    return string.Format("{0}.{1}.{2}",day, month, year);
  }

}
Program 20.6    The struct Date and the method DayDifference with a value parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Person{

  private string name;
  private Date dateOfBirth;
  private Date? dateOfDeath;

  public Person (string name, Date dateOfBirth){
    this.name = name;
    this.dateOfBirth = dateOfBirth;
    this.dateOfDeath = null;
  }

  public string Name{
    get {return name;}
  }

  public Date DateOfBirth{
    get {return dateOfBirth;}
  }

  public int DayAge(){
    return new Date(2007, 9, 25).DayDifference(dateOfBirth);   
  }                                                            

  public override string ToString(){
    return "Person: " + name + " " + dateOfBirth;
  }

}
Program 20.7    The class Person which calls DayDifference on dateOfBirth.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

class Client{

  public static void Main(){

    Person p = new Person("Hanne", new Date(1926, 12, 24));
    Console.WriteLine("{0}", p);

    int age = p.DayAge();   
                            
    Console.WriteLine("{0} of age {1}", p, age);
  }

}
Program 20.8    A client of Person and Date which reveal the consequences.

1
2
Person: Hanne 24.12.1926
Person: Hanne 24.12.1926 of age ...
Listing 20.9    Client program output.

Notice the following observation.

There is a good fit between use of value types and call-by-value parameter passing

If you wish the best possible fit (and no surprises) you should use value parameters with value types. The use of struct Date instead of class Date also alleviates the privacy leak problem, as pointed out in Section 16.5. See also Exercise 4.3.


Exercise 5.4. Passing struct values as ref parameters

This exercise corresponds to the similar exercise on the previous slide.

In the Date struct and the Person class of this slide we pass a struct value as a value parameter to the method DayDifference.

Assume in this exercise that the formal parameter other in Date.DayDifference is passed by reference (a C# ref parameter). Similarly, the actual parameter dateOfBirth to DayDifference should (of course) be passed by reference (using the keyword ref in front of the actual parameter).

What will be the difference caused by this program modification. You should compare with the version on on the slide.

Test-drive the program with a suitable client to verify your answer.

Solution


 

20.6.  Reference Parameters
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

In C, call-by-reference parameters are obtained by passing pointers as value parameters. Reference parameters in C# are not the same as call-by-reference parameters in C.

Reference parameters in C# are modeled after var parameters in Pascal. Stated briefly, a formal reference parameter in C# is an alias of the corresponding actual parameter. Therefore, the actual parameter must be a variable.

Reference parameters can be used for both input to and output from methods

Reference parameters can be used to establish alternative names (aliases) of already existing variables. The alternative names are used as formal parameters. Once established, such parameters can be used for both input and output purposes relative to a method call. The established aliases exist until the method returns to its caller.

If we - in C# - only are interested in using reference parameters for output purposes we should use out parameters, see Section 20.7.

Reference parameters work in the following way:

  • The corresponding argument must be a variable, and it must have a value

  • The types of the formal parameter and the argument must be identical

  • The formal parameter becomes another name (an alias) of the argument

  • The actual parameter must be prefixed with the keyword ref

In the first item it is stated that an actual reference parameter (which is a variable) must have a value before it is passed. In C#, this is called definite assignment.

As described in the fourth item, and as a novel contribution of C#, it is necessary to mark both formal and actual parameter with the ref keyword. In most other languages, only the formal parameter is marked. This may seem to be a little detail, but it implies that it is easy to spot reference parameters in a method calling form. This is very useful.

We show an example of reference parameters in Program 20.10: Swapping the values of two variables. This is the example used over and over, when reference parameters are explained.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;

public class A{
  private int a, b, c;

  public A(){
    a = 1; b = 2; c = 3;
  }

  public void Swap(ref int v1, ref int v2){   
    int temp;
    temp = v1; v1 = v2; v2 = temp;
  }
 
  public override string ToString(){
    return String.Format("{0} {1} {2}", a, b, c);
  }

  public void Go(){
    Console.WriteLine("{0}", this);
    Swap(ref a, ref b); Swap(ref b, ref c);  
    Console.WriteLine("{0}", this);
  }    

  public static void Main(){
    new A().Go();
  }
}
Program 20.10    The class A with a Swap method.

In Program 20.10 we instantiate the class itself (class A) in the Main method. We send the parameterless message Go to this object. Hereby we take the transition from a "static situation" to an "object situation". Without this transition it would not have been possible to use the instance variables a, b, and c in class A. (They should instead have been static variables). The Go method pattern illustrated here is inspired from [Bishop04].

1
2
1 2 3
2 3 1
Listing 20.11    Output of class A with Swap.

It seems natural to support more than just value parameters in an ambitious, real-world programming language. But it is worth a consideration how much - and in which situations - to use it. We will discuss this in some details in Section 20.8.

 

20.7.  Output Parameters
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Output parameters in C# are reference parameters used only for output purposes.

Output parameters are used for output from methods. The method is supposed to assign values to output parameters.

Here follows the detailed rules of output parameter passing:

  • The corresponding argument must be a variable

  • The corresponding argument needs not to have a value on beforehand

  • The formal parameter should/must be assigned by the method

  • The formal parameter becomes another name (an alias) of the argument

  • The types of the formal parameter and the argument must be identical

  • The actual parameter must be prefixed with the keyword out

Notice the second item: It is not necessary that the actual parameter has a value before the call. In fact, the purpose of the out parameter is exactly to (re)initialize the actual out parameters. The method must ensure that the output parameter has a value (is definitely assigned) when it returns.

In Program 20.12 DoAdd returns the sum of the parameters v1, v2, and v3 in the last parameter v. The corresponding actual parameter r is initialized by the call to DoAdd in line 21 of Program 20.12. I wrote DoAdd to demonstrate output parameters. Had it not been for this purpose, I would have returned the sum from DoAdd. In that way DoAdd should be called as result = DoAdd(v1, v2, v3). In this case I would de-emphasize the imperative nature by calling it just Add. These changes describe a transition from an imperative to a more functional programming style.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;

public class A{
  private int a, b, c;
  private int r;

  public A(){
    a = 1; b = 2; c = 3;
  }

  public void DoAdd(int v1, int v2, int v3, out int v){  
    v = v1 + v2 + v3;                                    
  }
 
  public override string ToString(){
    return String.Format("{0} {1} {2}. {3}", a, b, c, r);
  }

  public void Go(){
    Console.WriteLine("{0}", this);
    DoAdd(a, b, c, out r);   
    Console.WriteLine("{0}", this);
  }    

  public static void Main(){
    new A().Go();
  }
}
Program 20.12    The class A with a DoAdd method.

1
2
1 2 3. 0
1 2 3. 6
Listing 20.13    Output of class A with DoAdd.

In Program 20.14 of Section 20.9 we show a variant of Program 20.12, which allows for an arbitrary number of actual parameters. This variant of the program is introduced with the purpose of illustrating parameter arrays.

 

20.8.  Use of ref and out parameters in OOP
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

It is interesting to wonder about the fit between object-oriented programming and the use of reference parameters. Therefore the following question is relevant.

How useful are reference and output parameters in object-oriented programming?

Output parameters are useful in case we program a method which need to produce two or more pieces of output. In such a situation, we face the following possibilities:

  • Use a number of out parameters

  • Mutate objects passed by value parameters

  • Aggregate the pieces of output in an object and return it

  • Assign the output to instance variables which subsequently can be accessed by the caller

Let us first face the first item. If a (public) method needs to pass back (to its caller) more than one piece of output, which are only loosely related to each other, it may be the best solution to use one or more out parameters for these. It should be considered to pass one of the results back via return.

In a language with ref and out parameters it is confusing to pass results out of method via references passed by value (call-by-value). Use a ref or an out parameter!

If the pieces of output are related - if they together form a concept - it may be reasonable to aggregate the pieces of output in a new struct or a new class. An instance of the new type can then be returned via return.

Assignment of multiple pieces of output to instance variables in the enclosing class, and subsequent access of these via properties, may compromise the original idea of the class to which the method belongs. It may also pollute the class. Therefore, it should be avoided.

ref and out parameters are relatively rare in the C# standard libraries

In summary, we see that there are several alternatives to the use of reference and output parameters in an object-oriented context.

Reference (ref) and output (out) parameters are only used in very few methods in the .NET framework libraries. In general, it seems to be the case that reference parameters and output parameters are not central to object-oriented programming.

 

20.9.  Parameter Arrays
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

In the previous sections we have discussed various parameter passing techniques. They were all related to the meaning of the formal parameter relative to the corresponding actual parameter (and the argument derived from the actual parameter).

In this section we will concentrate on the parameter correspondence mechanism. In the parameter passing discussed until now there has been a one-to-one correspondence between formal parameters and actual parameters. In this section we will study a parameter passing technique where zero, one, or more actual parameters correspond to a single formal parameter.

A parameter array is a formal parameter of array type which can absorb zero, one or more actual parameters

A formal parameter list in C# starts with a number of value/reference/output parameters for which there must exist corresponding actual parameters in a method activation. Following these parameters there can be a single parameter array, which collects all remaining arguments in an array.

With parameter arrays, there can be arbitrary many actual parameters following the 'ordinary parameters', but not arbitrary few. There must always be so many actual parameters that all the 'required' formal parameters (before a possible parameter arrays) are associated.

The following rules pertain to use of parameter arrays in C#:

  • Elements of a parameter array are passed by value

  • A parameter array must be the last parameter in the formal parameter list

  • The formal parameter must be a single dimensional array

  • Arguments that follow ordinary value, ref and out parameters are put into a newly allocated array object

  • Arguments are implicitly converted to the element type of the array

It is easiest to understand parameter arrays from an example, such as Program 20.14. This is a variant of Program 20.12, which we have already discussed in Section 20.7. The DoAdd method takes one required parameter (which is an output parameter). As the actual out parameter in line 24, 27, and 30 we use the instance variable r. Following this parameter comes the parameter array called iv (for input values). All actual parameters after the first one must be of type int, and they are collected and inserted into an integer array, and made available to DoAdd as the int array iv.

In Program 20.14 the DoAdd messages to the current object add various combinations of the instance variables a, b, and c together. The result is assigned to the out parameter of DoAdd.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;

public class A{
  private int a, b, c;
  private int r;

  public A(){
    a = 1; b = 2; c = 3;
  }

  public void DoAdd(out int v, params int[] iv){  
    v = 0;  
    foreach(int i in iv)
      v += i;
  }
 
  public override string ToString(){
    return String.Format("{0} {1} {2}. {3}", a, b, c, r);
  }

  public void Go(){
    Console.WriteLine("{0}", this);

    DoAdd(out r, a, b, c);  
    Console.WriteLine("{0}", this);

    DoAdd(out r, a, b, c, a, b, c);  
    Console.WriteLine("{0}", this);

    DoAdd(out r); 
    Console.WriteLine("{0}", this);
  }    

  public static void Main(){
    new A().Go();
  }
}
Program 20.14    The class A with a DoAdd method - both out and params.

The output of Program 20.14 is shown in Listing 20.15. We have emphasized the value of r after each DoAdd message. Be sure that you are able to understand the parameter passing details.

1
2
3
4
1 2 3. 0  
1 2 3. 6  
1 2 3. 12  
1 2 3. 0  
Listing 20.15    Output of class A with DoAdd - both out and params.

The type constraint on the actual parameters can be 'removed' by having a formal parameter array of type Object[].

When we studied nullable types in Section 14.9 we encountered the IntSequence class. In line 5 of Program 14.17 you can see a parameter array of the constructor. Please notice the flexibility it gives in the construction of new IntSequence objects. See also Program 14.19 where IntSequence is instantiated with use of the mentioned constructor.

 

20.10.  Extension Methods
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

In C#3.0 an extension method defines an instance method in an existing class without altering the definition of the class. The use of extension methods is convenient if you do not have access to the source code of the class, in which we want to have a new instance method.

Let us look at an example in order to illustrate the mechanisms behind the class extension. We use Program 11.3 as the starting point. In line 26-31 of Program 11.3 it can be observed that we have inlined calculation of distances between pairs of points. It would embellish the program if we had called p.DistanceTo(q) instead of

   Math.Sqrt((p.x - q.x) * (p.x - q.x) + 
             (p.y - q.y) * (p.y - q.y));

to find the distance between to points p and q. We will now assume that the instance method DistanceTo can be used on an instance of class Point. Program 20.16 below shows the embellishment of Program 11.3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// A client of Point that instantiates three points and calculates
// the circumference of the implied triangle.

using System;

public class Application{

  public static void Main(){

    Point  p1 = PromptPoint("Enter first point"),
           p2 = PromptPoint("Enter second point"),
           p3 = PromptPoint("Enter third point");

    double p1p2Dist = p1.DistanceTo(p2),   
           p2p3Dist = p2.DistanceTo(p3),   
           p3p1Dist = p3.DistanceTo(p1);   

    double circumference = p1p2Dist + p2p3Dist + p3p1Dist;

    Console.WriteLine("Circumference: {0} {1} {2}: {3}", 
                       p1, p2, p3, circumference);
  }

  public static Point PromptPoint(string prompt){
    Console.WriteLine(prompt);
    double x = double.Parse(Console.ReadLine()),
           y = double.Parse(Console.ReadLine());
    return new Point(x,y, Point.PointRepresentation.Rectangular);
  }

}
Program 20.16    A Client of class Point which uses an extension method DistanceTo.

It is possible to extend class Point with the instance method DistanceTo without altering the source code of class Point. In a static class, such as in class PointExtensions shown below in Program 20.17, we define the DistanceTo method. It is defined as a static method with a first parameter prefixed with the keyword this. The C#3.0 compiler is able to translate an expression like q.DistanceTo(q) to PointExtensions.DistanceTo(p,q). This is the noteworthy "trick" behind extension methods.

1
2
3
4
5
6
7
8
9
10
using System;

public static class PointExtensions{   

  public static double DistanceTo(this Point p1, Point p2){   
    return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) +          
                     (p1.Y - p2.Y) * (p1.Y - p2.Y));          
  }                                                           

}
Program 20.17    The static class PointExtensions.

In summary, an extension method

  • extends the type of the first parameter

  • is defined as a static method with a this modifier on the first parameter

  • must be defined in a static class

  • cannot access private instance variables in the extended class

  • is called by - behind the scene - translating to a call of the static method


Exercise 5.5. Extending struct Double

At the accompanying page we have seen how class Point can be extended with the instance method DistanceTo. This is an extension method.

If you study the method DistanceTo you will see that we use the squareroot function Math.Sqrt, defined statically in class Math. And we could/should have used a similar Square function had it been available.

It is remarkable that C# 3.0 allows us to extend the structs behind the primitive types, such as Double and Int32.

Write a static class that extends struct Double with the two extension methods Sqrt and Square. Afterwards, rewrite the method DistanceTo such that it makes use of the new instance methods in struct Double.

Solution


 

20.11.  Methods versus Properties versus Indexers
Contents   Up Previous Next   Slide Annotated slide Aggregated slides    Subject index Program index Exercise index 

Here at the end of the chapter about methods we will summarize some rule of thumbs for choosing between properties, indexers and methods in a class:

  • Properties

    • For reading and extracting of individual instance/class variables

    • For writing and assigning individual instance/class variables

    • For other kinds of data access that does not involve time consuming computations

  • Indexers

    • Like properties

    • Used when it is natural to access data by indexes - array notation - instead of simple names

    • Used as surface notation for associative arrays

  • Methods

    • For all other operations that encapsulate calculations on the data of the class

 

20.12.  References
[Bishop04]Judith Bishop and Nigel Horspool, C# Concisely. Pearson. Addison Wesley, 2004.

Generated: Monday February 7, 2011, 12:15:34
Theme index -- Keyboard shortcut: 'u'  Previous theme in this lecture -- Keyboard shortcut: 'p'  Next slide in this lecture -- Keyboard shortcut: 'n'