Thủ tục Pascal miễn phí


________________________________________________________________________________
Hướng dẫn Lập trình viên về Free Pascal, Phiên bản 3. 0. 4
Tài liệu phiên bản 3. 0. 4

____________________________________________________________________________________

Một ý tưởng quan trọng khác được Pascal nhấn mạnh là khái niệm thủ tục, về cơ bản là một chuỗi các câu lệnh có tên duy nhất, có thể được kích hoạt nhiều lần bằng cách sử dụng tên của chúng. Bằng cách này, bạn tránh lặp đi lặp lại cùng một câu lệnh và có một phiên bản mã duy nhất, bạn có thể dễ dàng sửa đổi nó trên toàn bộ chương trình. Từ quan điểm này, bạn có thể coi các thường trình là cơ chế đóng gói mã cơ bản. Tôi sẽ quay lại chủ đề này với một ví dụ sau khi tôi giới thiệu cú pháp các thường trình Pascal

Các thủ tục và hàm Pascal

Trong Pascal, một thường trình có thể có hai dạng. một thủ tục và một chức năng. Về lý thuyết, một thủ tục là một thao tác mà bạn yêu cầu máy tính thực hiện, một hàm là một phép tính trả về một giá trị. Sự khác biệt này được nhấn mạnh bởi thực tế là một hàm có một kết quả, một giá trị trả về, trong khi một thủ tục thì không. Cả hai loại thường trình có thể có nhiều tham số, của các loại dữ liệu nhất định

Tuy nhiên, trong thực tế, sự khác biệt giữa hàm và thủ tục là rất hạn chế. bạn có thể gọi một hàm để thực hiện một số công việc và sau đó bỏ qua kết quả (có thể là mã lỗi tùy chọn hoặc thứ gì đó tương tự) hoặc bạn có thể gọi một thủ tục truyền kết quả vào trong các tham số của nó (thêm về các tham số tham chiếu ở phần sau của chương này)

Dưới đây là các định nghĩa của một thủ tục và hai phiên bản của cùng một chức năng, sử dụng một cú pháp hơi khác

procedure Hello;
begin
  ShowMessage ('Hello world!');
end;

function Double (Value: Integer) : Integer;
begin
  Double := Value * 2;
end;

// or, as an alternative
function Double2 (Value: Integer) : Integer;
begin
  Result := Value * 2;
end;

Theo tôi, việc sử dụng Kết quả thay vì tên hàm để gán giá trị trả về của một hàm đang trở nên khá phổ biến và có xu hướng làm cho mã dễ đọc hơn

Khi các thủ tục này đã được xác định, bạn có thể gọi chúng một hoặc nhiều lần. Bạn gọi thủ tục để làm cho nó thực hiện nhiệm vụ của nó và gọi một hàm để tính giá trị

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;

Ghi chú. Hiện tại, đừng quan tâm đến cú pháp của hai thủ tục trên, đây thực sự là các phương thức. Chỉ cần đặt hai nút trên biểu mẫu Delphi, nhấp vào chúng tại thời điểm thiết kế và Delphi IDE sẽ tạo mã hỗ trợ phù hợp. Bây giờ bạn chỉ cần điền vào các dòng giữa bắt đầu và kết thúc. Để biên dịch mã ở trên, bạn cũng cần thêm một điều khiển Chỉnh sửa vào biểu mẫu

Bây giờ chúng ta có thể quay lại khái niệm mã đóng gói mà tôi đã giới thiệu trước đây. Khi bạn gọi hàm Double, bạn không cần biết thuật toán được sử dụng để thực hiện nó. Nếu sau này bạn tìm ra cách tốt hơn để nhân đôi số, bạn có thể dễ dàng thay đổi mã của hàm, nhưng mã gọi sẽ không thay đổi (mặc dù việc thực hiện sẽ nhanh hơn. ). Nguyên tắc tương tự có thể được áp dụng cho thủ tục Hello. Chúng ta có thể sửa đổi đầu ra của chương trình bằng cách thay đổi mã của quy trình này và phương thức Button2Click sẽ tự động thay đổi tác dụng của nó. Đây là cách chúng ta có thể thay đổi mã

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;

Mẹo. Khi bạn gọi một hàm hoặc thủ tục Delphi hiện có hoặc bất kỳ phương thức VCL nào, bạn nên nhớ số lượng và loại tham số. Trình soạn thảo Delphi giúp bạn bằng cách đề xuất danh sách tham số của hàm hoặc thủ tục với gợi ý nhanh ngay khi bạn nhập tên và dấu ngoặc đơn mở. Tính năng này được gọi là Tham số mã và là một phần của công nghệ Code Insight

Thông số tham khảo

Các thủ tục Pascal cho phép truyền tham số theo giá trị và theo tham chiếu. Truyền tham số theo giá trị là mặc định. giá trị được sao chép vào ngăn xếp và thường trình sử dụng và thao tác với bản sao, không phải giá trị ban đầu

Truyền một tham số bằng tham chiếu có nghĩa là giá trị của nó không được sao chép vào ngăn xếp trong tham số chính thức của thủ tục (tránh sao chép thường có nghĩa là chương trình thực thi nhanh hơn). Thay vào đó, chương trình đề cập đến giá trị ban đầu, cũng nằm trong mã của thủ tục. Điều này cho phép thủ tục hoặc hàm thay đổi giá trị của tham số. Tham số truyền theo tham chiếu được thể hiện bằng từ khóa var

Kỹ thuật này có sẵn trong hầu hết các ngôn ngữ lập trình. Nó không có trong C, nhưng đã được giới thiệu trong C++, nơi bạn sử dụng ký hiệu & (chuyển qua tham chiếu). Trong Visual Basic, mọi tham số không được chỉ định là ByVal được chuyển theo tham chiếu

Dưới đây là một ví dụ về việc truyền tham số bằng tham chiếu bằng từ khóa var

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;

Trong trường hợp này, tham số được sử dụng để chuyển một giá trị cho thủ tục và trả về một giá trị mới cho mã gọi. Khi bạn viết

var
  X: Integer;
begin
  X := 10;
  DoubleTheValue (X);

giá trị của biến X trở thành 20, vì hàm sử dụng tham chiếu đến vị trí bộ nhớ ban đầu của X, ảnh hưởng đến giá trị ban đầu của nó

Truyền tham số theo tham chiếu có ý nghĩa đối với các loại thứ tự, đối với các chuỗi kiểu cũ và đối với các bản ghi lớn. Trên thực tế, các đối tượng Delphi luôn được truyền theo giá trị, bởi vì chính chúng là các tham chiếu. Vì lý do này, việc truyền một đối tượng theo tham chiếu không có ý nghĩa gì (ngoài những trường hợp rất đặc biệt), bởi vì nó tương ứng với việc truyền một "tham chiếu đến một tham chiếu. "

Chuỗi dài Delphi có hành vi hơi khác. chúng hoạt động như các tham chiếu, nhưng nếu bạn thay đổi một trong các biến chuỗi tham chiếu đến cùng một chuỗi trong bộ nhớ, chuỗi này sẽ được sao chép trước khi cập nhật nó. Một chuỗi dài được truyền dưới dạng tham số giá trị chỉ hoạt động như một tham chiếu về mức sử dụng bộ nhớ và tốc độ của thao tác. Nhưng nếu bạn sửa đổi giá trị của chuỗi, giá trị ban đầu không bị ảnh hưởng. Ngược lại, nếu bạn chuyển chuỗi dài theo tham chiếu, bạn có thể thay đổi giá trị ban đầu

Delphi 3 giới thiệu một loại tham số mới, out. Tham số out không có giá trị ban đầu và chỉ được sử dụng để trả về giá trị. Các tham số này chỉ nên được sử dụng cho các thủ tục và chức năng COM; . Ngoại trừ việc không có giá trị ban đầu, các tham số out hoạt động giống như các tham số var

Tham số không đổi

Để thay thế cho tham số tham chiếu, bạn có thể sử dụng tham số const. Vì bạn không thể gán một giá trị mới cho một tham số không đổi bên trong thường trình, nên trình biên dịch có thể tối ưu hóa việc truyền tham số. Trình biên dịch có thể chọn cách tiếp cận tương tự như tham số tham chiếu (hoặc tham chiếu const trong thuật ngữ C++), nhưng hành vi sẽ vẫn tương tự như tham số giá trị, vì giá trị ban đầu sẽ không bị ảnh hưởng bởi quy trình

Trong thực tế, nếu bạn cố gắng biên dịch mã (ngớ ngẩn) sau đây, Delphi sẽ báo lỗi

function DoubleTheValue (const Value: Integer): Integer;
begin
  Value := Value * 2;      // compiler error
  Result := Value;
end;

Mở tham số mảng

Khác với C, một hàm hay thủ tục Pascal luôn có một số tham số cố định. Tuy nhiên, có một cách để truyền một số tham số khác nhau cho một thủ tục bằng cách sử dụng một mảng mở

Định nghĩa cơ bản của tham số mảng mở là định nghĩa của một mảng mở đã nhập. Điều này có nghĩa là bạn chỉ ra kiểu của tham số nhưng không biết mảng sẽ có bao nhiêu phần tử kiểu đó. Đây là một ví dụ về một định nghĩa như vậy

function Sum (const A: array of Integer): Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := Low(A) to High(A) do
    Result := Result + A[I];
end;

Sử dụng High(A) chúng ta có thể lấy kích thước của mảng. Cũng lưu ý việc sử dụng giá trị trả về của hàm, Kết quả, để lưu trữ các giá trị tạm thời. Bạn có thể gọi hàm này bằng cách truyền cho nó một mảng các biểu thức Số nguyên

X := Sum ([10, Y, 27*I]);

Cho trước một mảng các Số nguyên, với kích thước bất kỳ, bạn có thể chuyển trực tiếp nó tới một quy trình yêu cầu tham số mảng mở hoặc thay vào đó, bạn có thể gọi hàm Slice để chỉ chuyển một phần của mảng (như được chỉ ra bởi tham số thứ hai của nó). Đây là một ví dụ, trong đó toàn bộ mảng được truyền dưới dạng tham số

________số 8

Nếu bạn chỉ muốn chuyển một phần của mảng cho hàm Slice, chỉ cần gọi nó theo cách này

X := Sum (Slice (List, 5));
Bạn có thể tìm thấy tất cả các đoạn mã được trình bày trong phần này trong ví dụ về OpenArr (xem Hình 6. 1, sau này, cho biểu mẫu)

Hình 6. 1. Ví dụ về OpenArr khi nhấn nút Partial Slice

Thủ tục Pascal miễn phí

Các mảng mở đã nhập trong Delphi 4 hoàn toàn tương thích với các mảng động (được giới thiệu trong Delphi 4 và được trình bày trong Chương 8). Mảng động sử dụng cú pháp giống như mảng mở, với điểm khác biệt là bạn có thể sử dụng một ký hiệu chẳng hạn như mảng Số nguyên để khai báo một biến, không chỉ để truyền một tham số

Tham số mảng mở biến thể loại

Bên cạnh các mảng mở đã nhập này, Delphi cho phép bạn xác định các mảng mở biến thể kiểu hoặc không nhập. Loại mảng đặc biệt này có số lượng giá trị không xác định, có thể thuận tiện cho việc truyền tham số

Về mặt kỹ thuật, mảng cấu trúc của const cho phép bạn chuyển một mảng có số phần tử không xác định thuộc các loại khác nhau vào một quy trình cùng một lúc. Ví dụ, đây là định nghĩa của hàm Format (chúng ta sẽ xem cách sử dụng hàm này trong Chương 7, bao gồm các chuỗi)

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
0

Tham số thứ hai là một mảng mở, có số lượng giá trị không xác định. Trên thực tế, bạn có thể gọi hàm này theo các cách sau

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
1

Lưu ý rằng bạn có thể truyền tham số dưới dạng giá trị hằng, giá trị của biến hoặc biểu thức. Khai báo một hàm kiểu này rất đơn giản, nhưng làm thế nào để bạn mã hóa nó?

Ghi chú. Đừng nhầm bản ghi TVarRec với bản ghi TVarData được sử dụng bởi chính loại Biến thể. Hai cấu trúc này có một mục đích khác nhau và không tương thích. Ngay cả danh sách các loại có thể cũng khác, vì TVarRec có thể chứa các loại dữ liệu Delphi, trong khi TVarData có thể chứa các loại dữ liệu OLE

Bản ghi TVarRec có cấu trúc như sau

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
2

Mỗi bản ghi có thể có trường VType, mặc dù ban đầu điều này không dễ thấy vì nó chỉ được khai báo một lần, cùng với dữ liệu kích thước Số nguyên thực tế (thường là tham chiếu hoặc con trỏ)

Sử dụng thông tin này, chúng ta thực sự có thể viết một hàm có khả năng hoạt động trên các loại dữ liệu khác nhau. Trong ví dụ về hàm SumAll, tôi muốn có thể tính tổng các giá trị thuộc các loại khác nhau, chuyển đổi chuỗi thành số nguyên, ký tự thành giá trị thứ tự tương ứng và thêm 1 cho giá trị Boolean thực. Mã này dựa trên một câu lệnh tình huống và khá đơn giản, mặc dù chúng ta phải hủy đăng ký các con trỏ khá thường xuyên

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
3

Tôi đã thêm mã này vào ví dụ OpenArr, gọi hàm SumAll khi nhấn một nút nhất định

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
4

Bạn có thể thấy đầu ra của lời gọi này, và dạng của ví dụ OpenArr, trong Hình 6. 2

Hình 6. 2. Dạng của ví dụ OpenArr, với hộp thông báo được hiển thị khi nhấn nút Untyped

Thủ tục Pascal miễn phí

Công ước gọi Delphi

Phiên bản 32 bit của Delphi đã giới thiệu một cách tiếp cận mới để truyền tham số, được gọi là fastcall. Bất cứ khi nào có thể, tối đa ba tham số có thể được chuyển vào các thanh ghi CPU, giúp việc gọi hàm nhanh hơn nhiều. Quy ước gọi nhanh (được sử dụng theo mặc định trong Delphi 3) được biểu thị bằng từ khóa register

Vấn đề là đây là quy ước mặc định và các chức năng sử dụng nó không tương thích với Windows. các chức năng của API Win32 phải được khai báo bằng cách sử dụng quy ước gọi stdcall, hỗn hợp của quy ước gọi Pascal ban đầu của API Win16 và quy ước gọi cdecl của ngôn ngữ C

Nhìn chung, không có lý do gì để không sử dụng quy ước gọi nhanh mới, trừ khi bạn đang thực hiện các lệnh gọi Windows bên ngoài hoặc xác định các chức năng gọi lại của Windows. Chúng ta sẽ xem một ví dụ sử dụng quy ước stdcall trước khi kết thúc chương này. Bạn có thể tìm thấy bản tóm tắt về các quy ước gọi Delphi trong chủ đề Quy ước gọi dưới phần trợ giúp của Delphi

Phương pháp là gì?

Nếu bạn đã từng làm việc với Delphi hoặc đọc tài liệu hướng dẫn, có lẽ bạn đã nghe về thuật ngữ "phương pháp". Phương thức là một loại hàm hoặc thủ tục đặc biệt có liên quan đến một kiểu dữ liệu, một lớp. Trong Delphi, mỗi khi xử lý một sự kiện, chúng ta cần định nghĩa một phương thức, thường là thủ tục. Tuy nhiên, nói chung, thuật ngữ phương thức được sử dụng để chỉ cả các chức năng và thủ tục liên quan đến một lớp.

Chúng ta đã thấy một số phương thức trong các ví dụ ở chương này và các chương trước. Đây là một phương thức rỗng được Delphi tự động thêm vào mã nguồn của một biểu mẫu

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
5

Tuyên bố chuyển tiếp

Khi bạn cần sử dụng một định danh (bất kỳ loại nào), trình biên dịch phải nhìn thấy một số loại khai báo để biết định danh đề cập đến cái gì. Vì lý do này, bạn thường cung cấp một khai báo đầy đủ trước khi sử dụng bất kỳ quy trình nào. Tuy nhiên, có những trường hợp điều này là không thể. Nếu thủ tục A gọi thủ tục B và thủ tục B gọi thủ tục A, khi bạn bắt đầu viết mã, bạn sẽ cần gọi một thường trình mà trình biên dịch vẫn chưa thấy khai báo

Nếu bạn muốn khai báo sự tồn tại của một thủ tục hoặc hàm với một tên nhất định và các tham số đã cho mà không cung cấp mã thực của nó, bạn có thể viết thủ tục hoặc hàm theo sau là từ khóa forward

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
6

Sau này, mã sẽ cung cấp một định nghĩa đầy đủ về thủ tục, nhưng điều này có thể được gọi ngay cả trước khi nó được định nghĩa đầy đủ. Đây là một ví dụ ngớ ngẩn, chỉ để cung cấp cho bạn ý tưởng

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
7

Cách tiếp cận này cho phép bạn viết đệ quy lẫn nhau. DoubleHello gọi Hello, nhưng Hello cũng có thể gọi DoubleHello. Tất nhiên phải có điều kiện để kết thúc đệ quy, tránh tràn stack. Bạn có thể tìm thấy mã này, với một số thay đổi nhỏ, trong ví dụ DoubleH

Mặc dù khai báo thủ tục chuyển tiếp không phổ biến lắm trong Delphi, nhưng có một trường hợp tương tự xảy ra thường xuyên hơn nhiều. Khi bạn khai báo một thủ tục hoặc hàm trong phần giao diện của một đơn vị (thêm về các đơn vị trong chương tiếp theo), nó được coi là khai báo chuyển tiếp, ngay cả khi không có từ khóa chuyển tiếp. Trên thực tế, bạn không thể viết phần thân của một thói quen trong phần giao diện của một đơn vị. Đồng thời, bạn phải cung cấp trong cùng một đơn vị việc thực hiện từng thói quen mà bạn đã khai báo

Điều tương tự cũng xảy ra đối với việc khai báo một phương thức bên trong một loại lớp do Delphi tự động tạo ra (khi bạn thêm một sự kiện vào một biểu mẫu hoặc các thành phần của nó). Trình xử lý sự kiện được khai báo bên trong lớp TForm là các khai báo chuyển tiếp. mã sẽ được cung cấp trong phần triển khai của đơn vị. Đây là đoạn trích mã nguồn của một ví dụ trước đó, với phần khai báo của phương thức Button1Click

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
8

Các loại thủ tục

Một tính năng độc đáo khác của Object Pascal là sự hiện diện của các loại thủ tục. Đây thực sự là một chủ đề ngôn ngữ nâng cao, mà chỉ một số lập trình viên Delphi sẽ sử dụng thường xuyên. Tuy nhiên, vì chúng ta sẽ thảo luận về các chủ đề liên quan trong các chương sau (cụ thể là con trỏ phương thức, một kỹ thuật được Delphi sử dụng nhiều), bạn nên xem nhanh chúng ở đây. Nếu bạn là một lập trình viên mới làm quen, bạn có thể bỏ qua phần này ngay bây giờ và quay lại khi bạn cảm thấy sẵn sàng

Trong Pascal có khái niệm kiểu thủ tục (tương tự như khái niệm con trỏ hàm của ngôn ngữ C). Việc khai báo một kiểu thủ tục cho biết danh sách các tham số và, trong trường hợp của một hàm, kiểu trả về. Ví dụ: bạn có thể khai báo một loại thủ tục mới, với tham số Số nguyên được truyền bằng tham chiếu, với mã này

procedure TForm1.Button1Click (Sender: TObject);
begin
  Hello;
end;
 
procedure TForm1.Button2Click (Sender: TObject);
var
  X, Y: Integer;
begin
  X := Double (StrToInt (Edit1.Text));
  Y := Double (X);
  ShowMessage (IntToStr (Y));
end;
9

Loại thủ tục này tương thích với bất kỳ thủ tục nào có cùng tham số (hoặc cùng chữ ký hàm, để sử dụng biệt ngữ C). Đây là một ví dụ về một thói quen tương thích

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
0

Ghi chú. Trong phiên bản 16-bit của Delphi, các thường trình phải được khai báo bằng cách sử dụng lệnh far để được sử dụng làm giá trị thực của một loại thủ tục

Các loại thủ tục có thể được sử dụng cho hai mục đích khác nhau. bạn có thể khai báo các biến thuộc loại thủ tục hoặc chuyển một loại thủ tục (nghĩa là con trỏ hàm) làm tham số cho một thủ tục khác. Với các khai báo thủ tục và loại trước đó, bạn có thể viết mã này

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
1

Mã này có tác dụng tương tự như phiên bản ngắn hơn sau

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
2

Phiên bản đầu tiên rõ ràng phức tạp hơn, vậy tại sao chúng ta nên sử dụng nó? . Có thể xây dựng một ví dụ phức tạp thể hiện cách tiếp cận này. Tuy nhiên, tôi muốn để bạn khám phá một cái khá đơn giản, tên là ProcType. Ví dụ này phức tạp hơn những ví dụ chúng ta đã thấy cho đến nay, để làm cho tình huống thực tế hơn một chút

Đơn giản chỉ cần tạo một dự án trống và đặt hai nút radio và một nút ấn, như thể hiện trong Hình 6. 3. Ví dụ này dựa trên hai thủ tục. Một thủ tục được sử dụng để nhân đôi giá trị của tham số. Quy trình này tương tự như phiên bản tôi đã trình bày trong phần này. Thủ tục thứ hai được sử dụng để nhân ba giá trị của tham số và do đó được đặt tên là TripleTheValue

Hình 6. 3. Hình thức của ví dụ ProcType

Thủ tục Pascal miễn phí

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
3

Cả hai thủ tục hiển thị những gì đang xảy ra, để cho chúng tôi biết rằng chúng đã được gọi. Đây là một tính năng sửa lỗi đơn giản mà bạn có thể sử dụng để kiểm tra xem một phần mã nhất định có được thực thi hay không, thay vì thêm một điểm ngắt trong đó.

Mỗi lần người dùng nhấn nút Áp dụng, một trong hai quy trình sẽ được thực hiện, tùy thuộc vào trạng thái của các nút radio. Trên thực tế, khi bạn có hai nút radio trong một biểu mẫu, mỗi lần chỉ có thể chọn một trong số chúng. Mã này có thể đã được triển khai bằng cách kiểm tra giá trị của các nút radio bên trong mã cho sự kiện OnClick của nút Áp dụng. Để chứng minh việc sử dụng các loại thủ tục, thay vào đó tôi đã sử dụng một cách tiếp cận dài hơn nhưng thú vị. Mỗi lần người dùng nhấp vào một trong hai nút radio, một trong các quy trình được lưu trữ trong một biến

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
4

Khi người dùng nhấp vào nút ấn, quy trình chúng tôi đã lưu trữ được thực thi

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
5

Để cho phép ba hàm khác nhau truy cập các biến IP và X, chúng ta cần làm cho chúng hiển thị trên toàn bộ biểu mẫu; . Một giải pháp cho vấn đề này là đặt các biến này bên trong khai báo biểu mẫu

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
6

Chúng ta sẽ thấy chính xác điều này có nghĩa là gì trong chương tiếp theo, nhưng hiện tại, bạn cần sửa đổi mã do Delphi tạo cho loại lớp như đã chỉ ra ở trên và thêm định nghĩa về loại thủ tục mà tôi đã trình bày trước đây. Để khởi tạo 2 biến này với giá trị phù hợp, chúng ta có thể xử lý sự kiện OnCreate của form (chọn sự kiện này trong Object Inspector sau khi đã kích hoạt form, hoặc chỉ cần double-click vào form). Tôi khuyên bạn nên tham khảo danh sách để nghiên cứu chi tiết về mã nguồn của ví dụ này

Bạn có thể xem một ví dụ thực tế về việc sử dụng các loại thủ tục trong Chương 9, trong phần A Windows Callback Function

Quá tải chức năng

Ý tưởng quá tải rất đơn giản. Trình biên dịch cho phép bạn định nghĩa hai hàm hoặc thủ tục sử dụng cùng một tên, miễn là các tham số khác nhau. Trên thực tế, bằng cách kiểm tra các tham số, trình biên dịch có thể xác định phiên bản nào của thủ tục mà bạn muốn gọi

Hãy xem xét chuỗi hàm này được trích xuất từ ​​đơn vị Toán học của VCL

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
7

Khi bạn gọi Min (10, 20), trình biên dịch dễ dàng xác định rằng bạn đang gọi hàm đầu tiên của nhóm, vì vậy giá trị trả về sẽ là Số nguyên

Các quy tắc cơ bản là hai

  • Mỗi phiên bản của thói quen phải được theo sau bởi từ khóa quá tải
  • Sự khác biệt phải ở số lượng hoặc loại tham số hoặc cả hai. Thay vào đó, kiểu trả về không thể được sử dụng để phân biệt giữa hai thường trình

Đây là ba phiên bản quá tải của thủ tục ShowMsg mà tôi đã thêm vào ví dụ OverDef (một ứng dụng thể hiện quá tải và các tham số mặc định)

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
8

Ba chức năng hiển thị hộp thông báo có chuỗi, sau khi tùy chọn định dạng chuỗi theo các cách khác nhau. Đây là ba cuộc gọi của chương trình

procedure Hello;
begin
  MessageDlg ('Hello world!', mtInformation, [mbOK]);
end;
9

Điều khiến tôi ngạc nhiên theo một cách tích cực là công nghệ Tham số mã của Delphi hoạt động rất độc đáo với các thủ tục và hàm quá tải. Khi bạn nhập dấu ngoặc đơn mở sau tên quy trình, tất cả các lựa chọn thay thế khả dụng sẽ được liệt kê. Khi bạn nhập các tham số, Delphi sử dụng loại của chúng để xác định những lựa chọn thay thế nào vẫn khả dụng. Trong Hình 6. 4, bạn có thể thấy rằng sau khi bắt đầu nhập một chuỗi hằng số, Delphi chỉ hiển thị các phiên bản tương thích (bỏ qua phiên bản của thủ tục ShowMsg có tham số đầu tiên là số nguyên)

Hình 6. 4. Nhiều lựa chọn thay thế do Tham số mã cung cấp cho các quy trình quá tải được lọc theo các tham số đã có sẵn

Thủ tục Pascal miễn phí

Thực tế là mỗi phiên bản của một quy trình quá tải phải được đánh dấu chính xác ngụ ý rằng bạn không thể quá tải một quy trình hiện có của cùng một đơn vị không được đánh dấu bằng từ khóa quá tải. (Thông báo lỗi bạn nhận được khi thử là. "Tuyên bố trước của '' không được đánh dấu bằng chỉ thị 'quá tải'. ") Tuy nhiên, bạn có thể quá tải một thói quen ban đầu được khai báo trong một đơn vị khác. Điều này là để tương thích với các phiên bản trước của Delphi, cho phép các đơn vị khác nhau sử dụng lại cùng một tên thủ tục. Dù sao, hãy lưu ý rằng trường hợp đặc biệt này không phải là một tính năng bổ sung của quá tải, mà là dấu hiệu của các vấn đề bạn có thể gặp phải

Ví dụ: bạn có thể thêm vào một đơn vị đoạn mã sau

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
0

Mã này không thực sự làm quá tải thói quen MessageDlg ban đầu. Trong thực tế nếu bạn viết

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
1

bạn sẽ nhận được một thông báo lỗi đẹp mắt cho biết rằng một số tham số bị thiếu. Cách duy nhất để gọi phiên bản cục bộ thay vì phiên bản của VCL là tham chiếu rõ ràng đến đơn vị cục bộ, điều gì đó đánh bại ý tưởng quá tải

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
2

Thông số mặc định

Một tính năng mới có liên quan của Delphi 4 là bạn có thể cung cấp giá trị mặc định cho tham số của hàm và bạn có thể gọi hàm có hoặc không có tham số. Hãy để tôi chỉ ra một ví dụ. Chúng ta có thể định nghĩa đóng gói sau đây của phương thức MessageBox của đối tượng toàn cầu Ứng dụng, sử dụng PChar thay vì chuỗi, cung cấp hai tham số mặc định

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
3

Với định nghĩa này, chúng ta có thể gọi thủ tục theo từng cách sau

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
4

Trong Hình 6. 5, bạn có thể thấy rằng Tham số mã của Delphi sử dụng đúng một kiểu khác để biểu thị các tham số có giá trị mặc định, vì vậy bạn có thể dễ dàng xác định tham số nào có thể được bỏ qua

Hình 6. 5. Tham số mã của Delphi đánh dấu bằng dấu ngoặc vuông các tham số có giá trị mặc định;

Thủ tục Pascal miễn phí

Lưu ý rằng Delphi không tạo ra bất kỳ mã đặc biệt nào để hỗ trợ các tham số mặc định; . Các tham số còn thiếu được trình biên dịch thêm vào mã gọi một cách đơn giản

Có một hạn chế quan trọng ảnh hưởng đến việc sử dụng các tham số mặc định. Bạn không thể "bỏ qua" các thông số. Ví dụ: bạn không thể chuyển tham số thứ ba cho hàm sau khi bỏ qua tham số thứ hai

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
5

Đây là quy tắc chính cho các tham số mặc định. Trong một cuộc gọi, bạn chỉ có thể bỏ qua các tham số bắt đầu từ cuộc gọi cuối cùng. Nói cách khác, nếu bạn bỏ qua một tham số, bạn cũng phải bỏ qua những tham số sau

Cũng có một số quy tắc khác cho các tham số mặc định

  • Các tham số có giá trị mặc định phải ở cuối danh sách tham số
  • Giá trị mặc định phải là hằng số. Rõ ràng, điều này giới hạn các loại bạn có thể sử dụng với các tham số mặc định. Ví dụ, một mảng động hoặc một kiểu giao diện không thể có tham số mặc định khác nil;
  • Các tham số mặc định phải được truyền theo giá trị hoặc dưới dạng const. Tham số tham chiếu (var) không thể có giá trị mặc định

Sử dụng các tham số mặc định và quá tải cùng một lúc có thể gây ra một số vấn đề, vì hai tính năng có thể xung đột. Ví dụ: nếu tôi thêm vào ví dụ trước phiên bản mới sau đây của thủ tục ShowMsg

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
6

thì trình biên dịch sẽ không phàn nàn-đây là một định nghĩa hợp pháp. Tuy nhiên, cuộc gọi

procedure DoubleTheValue (var Value: Integer);
begin
  Value := Value * 2;
end;
7

được trình biên dịch gắn cờ là cuộc gọi quá tải mơ hồ tới 'ShowMsg'. Lưu ý rằng lỗi này xuất hiện trong một dòng mã được biên dịch chính xác trước định nghĩa quá tải mới. Trong thực tế, chúng ta không có cách nào để gọi thủ tục ShowMsg với một tham số chuỗi, vì trình biên dịch không biết liệu chúng ta muốn gọi phiên bản chỉ có tham số chuỗi hay phiên bản có tham số chuỗi và tham số nguyên với giá trị mặc định . Khi có nghi ngờ tương tự, trình biên dịch dừng lại và yêu cầu lập trình viên nói rõ hơn ý định của mình

Phần kết luận

Viết thủ tục và hàm là yếu tố then chốt của lập trình, mặc dù trong Delphi bạn sẽ có xu hướng viết các phương thức -- thủ tục và hàm được kết nối với các lớp và đối tượng

Tuy nhiên, thay vì chuyển sang các tính năng hướng đối tượng, một số chương tiếp theo sẽ cung cấp cho bạn một số chi tiết về các thành phần lập trình Pascal khác, bắt đầu với các chuỗi

Thủ tục trong Pascal là gì?

PASCAL - THỦ TỤC. Các thủ tục là chương trình con, thay vì trả về một giá trị đơn lẻ, cho phép thu được một nhóm kết quả . Xác định một thủ tục. Trong Pascal, một thủ tục được định nghĩa bằng từ khóa thủ tục.

Tham số hình thức trong Pascal là gì?

tham số chính thức — mã định danh được sử dụng trong một phương thức để đại diện cho giá trị được người gọi truyền vào phương thức . Ví dụ, số tiền là một tham số chính thức của processDeposit.

Các hàm tiêu chuẩn trong Pascal là gì?

Các hàm Pascal chuẩn là. Tên hàm . Mô tả . Loại đối số . Loại trả về .