• Aucun résultat trouvé

The case Statement

Dans le document Teach Yourself Borland Delphi 4 in 21 Days (Page 54-59)

The case statement can be considered a glorified if statement. It enables you to execute one of several code blocks based on the result of an expression. The expression might be a variable, the result of a function call, or any valid Object Pascal code that evaluates to an expression. Here is an example of a case statement:

case AmountOverSpeedLimit of

There are several parts that make up a case statement. First, you can see that there is the expression, which in this example is the variable AmountOverSpeedLimit (remember, I warned you about long variable names!). Next, the case statement tests the expression for equality. If AmountOverSpeedLimit equals 0 (0 :), the value 0 is assigned to the variable Fine. If

AmountOverSpeedLimit is equal to 10, a value of 20 is assigned to Fine, and so on. In each of the first three cases a value is assigned to Fine and code execution jumps out of the case statement, which means that a case matching the expression has been found and the rest of the case statement can be ignored.

Notice that cases 20 and 25 have commas following them, but no statements. If the expression AmountOverSpeedLimit evaluates to 20 or 25, those cases fall through and the next code block encountered will be executed. In this situation, values of 20, 25, or 30 will all result in the same code being executed.

Finally, you see the else statement. The code block following the else statement will be executed if no matching cases are found. Inclusion of the else statement is optional. You could write a case statement without an else:

case X of

10 : DoSomething;

20 : DoAnotherThing;

30 : TakeABreak;

end;

As I said earlier, you might want to use a case statement if you find that you have several if statements back to back. The case statement is a bit clearer to others reading your program.

NOTE: The expression portion of a case statement must evaluate to an Object Pascal ordinal type (Integer, Word, Byte, and so on). The following, for example, is not allowed:

case SomeStringVariable of `One' : { code }

`Two' : { code } end;

String values are not allowed, nor are floating-point values.

The case Statement case expr of

value_1: statements_1;

value_2: statements_2;

. . .

value_n: statements_n;

else

else_statements;

end;

The case statement offers a way to execute different blocks of code depending on various values of an expression expr. The block of code represented by statements_1 is executed when expr is equal to value_1, the block of code represented by statements_2 when expr is equal to value_2, and so on through the block of code represented by statements_n when expr is equal to value_n. When expr is not equal to any of the value_1 through value_n, the block of code at else_statements is executed. The else statement is optional.

Scope

The term scope refers to the visibility of variables within different parts of your program. Most variables have local scope, which means that the variable is visible only within the code block in which it is declared. Take a look at the program in Listing 2.1. (This is the first look you've had at a complete unit as generated by Delphi. There is some code here that you haven't seen before, and I'll explain it all in due time, but for the time being you can ignore the parts you aren't familiar with.) New Term: The term scope refers to the visibility of variables within different parts of your program.

LISTING 2.1. SCOPEU.PAS.

01: unit ScopeU;

02:

13: procedure Button1Click(Sender: TObject);

14: procedure FormCreate(Sender: TObject);

15: private

41: Memo1.Lines.Add(`Local Function X: ` + IntToStr(X));

42: end;

48: Memo1.Lines.Add(`Local X: ` + IntToStr(X));

49: { Unit scope X has a value of 200. }

50: Memo1.Lines.Add(`Global X: ` + IntToStr(ScopeU.X));

51: { Call the Test procedure. } 52: Test;

53: end;

54:

The first thing you might notice (if you're still awake by this time) is that the variable X is declared three times in this program. It is declared on line 27 in the implementation section, it is declared on line 33 in the Button1Click method,

and it is declared on line 37 in a Local Procedure called Test. If you accidentally declare a variable more than once, the compiler spits out an error that says

Identifier redeclared: `X', and the compile stops. Yet this program compiles and runs just

fine. Why? Because each of the X variables in Listing 2.1 has different scope.

Take a closer look at Listing 2.1. The declaration for X on line 37 is inside the local procedure Test and is local to that block of code. (I realize I haven't talked about local functions and procedures yet so I'm getting a bit ahead of myself again. Bear with me; I explain local functions later in the section "Local Functions and Procedures.") Effectively, the X that is declared on line 37 does not exist outside the Test procedure. This variable has local scope. Likewise, the declaration for X on line 33 is local to the Button1Click method and does not exist outside the function.

Now look at the variable X declared in the implementation section. This variable is visible anywhere in this unit. Think about that for a minute. Once inside the Button1Click procedure, there are two variables named X (the one declared in the

implementation section and the one declared in the Button1Click method), and both are in scope. Which one is being used?

The answer: the one in the Button1Click method, because it has the most immediate scope.

The variable X that is declared in the implementation section is said to have unit scope. What this means is that this variable X is available anywhere in the unit. As mentioned earlier, a local variable has precedence over a variable with unit scope. But what if you want to access the unit variable X from inside the Button1Click procedure and not the local variable X? You can qualify the variable. Line 50 of Listing 2.1 contains this line:

Memo1.Lines.Add(`Global X: ` + IntToStr(ScopeU.X));

As you can see, the variable X is qualified with the unit name (ScopeU) followed by the dot operator. Qualifying the variable with the unit name says, "Give me the unit variable X and not the local variable X." (The dot operator is also used with records and classes, but I'll get to that when I talk about classes later.)

As I said, when the unit variable X is declared in the implementation section, it has unit scope. If you want a variable to be available to other units in the project, you should declare the variable in the interface section (the variable Form1 in Listing 2.1 is declared in this way). A variable declared in the interface section can be accessed from other units in the project. A variable declared in this way is often referred to as a global variable. To access a variable declared in the interface section of a unit requires nothing more than adding the unit to the uses list and accessing the variable as you would any other variable. If any units in the project have variables with the same name, the variables can be qualified with the unit name as described earlier.

NOTE: I just said that a variable declared in a unit's interface section is usually referred to as a global variable.

That's not entirely accurate, though, because the variable cannot be automatically used by other units in the project--you have to add the unit containing the variable to the uses list of any other unit that wants to use the

variable. A true global variable is a variable that can be used by any unit in the program without the need to add the unit containing the variable to the uses list. Delphi has a few global variables set up by the compiler's startup code. You cannot declare true global variables yourself.

Records

A record is a collection of related data rolled up into a single storage unit. For instance, let's say you want to keep a mailing list. It would be convenient to use a single data variable to hold all the fields needed in a typical mailing list. A record enables you to do that. You first declare the record and then later create an instance of that record when you want to use the record. A record is declared with the record keyword:

MailingListRecord = record FirstName : string;

LastName : string;

Address : string;

City : string;

State : string;

Zip : Integer;

end;

Each of the elements in a record is called a field. Notice that each of the fields must be declared just as if it were a variable in a code block. This record example has five string fields and one integer field. (My apologies to my friends around the world if this looks like a U.S.-slanted mailing-list record.) A zip code/postal code field should really be a string as well, but I want to show you a record with more than one data type.

A record is a collection of related data identified as a single storage unit. After a record is declared, an instance of that record can be created for use. Each of the elements in a record is called a field.

NOTE: The record in this example is fine for what I am doing here, but it is not ideal for storing records in files.

When you store records in files, each record should be of the exact same size in bytes. Because the record in this example uses long strings as fields, there is no way to guarantee that the records will all be the same size. When creating records that will be stored in a file, you should use short strings or even an array of Char over long strings. I'll talk about this more tomorrow when I discuss file input and output in the section "Dealing with Binary Data."

Now that the record is declared, it can be put to use. I first need to create an instance of the record. Here's how that looks:

var

MLRecord : TMailingListRecord;

This statement allocates memory for the record and assigns that memory to a variable named Record. Now that I have an instance of the record set up, I can assign values to the fields:

MLRecord.FirstName := `Bruce';

MLRecord.LastName := `Reisdorph';

MLRecord.Address := `123 Inspiration Pt.';

MLRecord.City := `Merced';

MLRecord.State := `CA';

MLRecord.Zip := 99999;

This code snippet contains some syntax you haven't seen yet (although it is very similar to earlier examples when I was discussing qualifying variables). To access the fields of a record, you need to employ the structure member selector operator, commonly called the dot operator. The dot operator is a period placed between the variable name and the field name. If you forget to add the record member operator, you will probably find the compiler complaining about undefined symbols. The record member operator enables you to access a particular member of the record--either to read the value of the field or to change the value of the field. Here's an example of placing the contents of a particular field in a record into an label on a form:

Label1.Caption := MLRecord.LastName;

The record Statement

name = record

field_1 : data_type;

field_2 : data_type;

. . .

field_n : data_type;

end;

The record statement declares a grouping of fields (field_1, field_2, ..., field_n) and provides a name for this grouping (name).

Dans le document Teach Yourself Borland Delphi 4 in 21 Days (Page 54-59)