Bài viết: Hướng dẫn truyền tham số trong VBA (ByVal & ByRef)

Liên hệ QC

PhanTuHuong

VBA & VB.NET for Excel & AutoCad
Thành viên danh dự
Tham gia
13/6/06
Bài viết
7,120
Được thích
24,279
Bài này sẽ giải thích tại sao các bạn thỉnh thoảng có thấy khai báo ByVal trước biến chứ không phải là Dim thông thường.

  • Các biến số của hàm hay thủ tục có thể bị thay đổi bởi các quy trình tính toán, điều đó cho thấy sự quan trọng của việc bảo vệ giá trị gốc của biến.
  • Mặc định VB truyền thông tin tới hàm (hoặc thủ tục) dạng tham chiếu (ByRef), để tham chiếu tới dữ liệu gốc trong biến số của hàm vào lúc hàm được gọi ra. Hơn nữa, nếu hàm làm thay đổi giá trị của biến số, giá trị gốc sẽ bị thay đổi.

ByVal_vs_ByRef.JPG



  • Bạn sẽ có được kết quả trên nếu bạn bỏ qua từ khoá ByVal đứng trước biến số num1 trong hàm MyAverage trong phần khai báo biến số.
  • Nếu bạn muốn thủ tục hàm thay đổi giá trị gốc, bạn không cần thiết phải thêm từ khoá ByRef vào, vì VB đã mặc định truyền tham số là ByRef.
  • Khi bạn sử dụng từ khoá ByVal trước tên biến số, VBA sẽ truyền tham số bằng giá trị cho biến này. Điều đó có nghĩa là VBA sẽ tạo ra một bản sao của dữ liệu gốc. Bản sao đó sẽ được truyền tới hàm. Nếu hàm thay đổi giá trị của biến số được truyền tới, giá trị gốc sẽ không thay đổi - chỉ là copy sự thay đổi đó. Điều đó giải thích tại sao khi hàm MyAverage đã thay đổi giá trị của biến num1, nhưng giá trị gốc của biến đó vẫn như cũ.

Trong một số thủ tục khi khai báo bằng Dim thông thường, Visual Basic có thể thay đổi giá trị của các biến số. Ví dụ:

Mã:
Sub Thay_doi()
    Dim i, Giatri As Integer
    Giatri = 20                        'Lúc đầu thì biến Giatri là 20
    For i = 1 To 4
        Giatri = Giatri + 1
    Next
        MsgBox "Gia tri bay gio la " & Giatri     'Sau 4 lần vòng lặp, Giatri có giá trị mới là 24
End Sub

Để không làm thay đổi giá trị của biến số, bạn phải khai trước tên của biến số đó với từ khoá ByVal. Xem ví dụ sau để hiểu hơn:

Mã:
Sub ThreeNumbers()

Dim num1 As Integer, num2 As Integer, num3 As Integer

num1 = 10: num2 = 20: num3 = 30

MsgBox "Gia tri trung binh la " & MyAverage(num1, num2, num3)

MsgBox "num1= " & num1 & Chr(13) & "num2= " & num2 & Chr(13) & "num3= " & num3 & Chr(13)

End Sub

Mã:
Function MyAverage(ByVal num1, ByVal num2, ByVal num3)

num1 = num1 + 1

MyAverage = (num1 + num2 + num3) / 3

End Function

Thủ tục ThreeNumbers sẽ ấn định giá trị của ba biến số và sau đó gọi hàm MyAverage tính toán, trả về giá trị trung bình của các số đã được lưu giữ trong các biến số đó. Các đối số của hàm là những biến số num1, num2 và num3, toàn bộ tên đối số đều có từ khoá ByVal đứng trước. Thủ tục ThreeNumbers đã truyền tham số cho num1, num2, num3 của hàm MyAverage.

Khi tính giá trị trung bình, hàm MyAverage đã thay đổi giá trị của biến số num1. Biến số num1 có giá trị bằng 11 (10+1) ở trong hàm. Do vậy, khi hàm tính giá trị trung bình của thủ tục ThreeNumbers, hộp thông báo MsgBox sẽ hiển thị kết quả 20.333 và chứ không phải là 20. Sau đó MsgBox sẽ hiển thị toàn bộ từng giá trị biến số. Các giá trị đó được lưu giữ là giá trị gốc ấn định cho chúng (10, 20 và 30).

Việc gì sẽ xảy ra khi bạn bỏ từ khoá ByVal trước biến số num1 trong khai báo hàm MyAverage. Kết quả tính toán của hàm vẫn là giá trị trên, nhưng biến số num1 sẽ hiển thị bây giờ là 11. Hàm MyAverage trả về kết quả 20.333 và thay đổi giá trị gốc của số liệu lưu trong biến số num1.

Mã:
Function MyAverage(num1, ByVal num2, ByVal num3)

num1 = num1 + 1

MyAverage = (num1 + num2 + num3) / 3

End Function

Như vậy, để ngăn cản sự thay đổi giá trị cung cấp cho hàm số, bạn sử dụng từ khoá ByVal.


Xem thêm:
+ Byval: là từ viết tắt của By Value, nghĩa là truyền bằng trị. Phương pháp này không làm thay đổi giá trị của nội dung của biến khai báo bởi từ khóa Byval
+ Byref: là từ viết tắt của By Reference, nghĩa là truyền bằng tham chiếu. Phương pháp này cho phép thay đổi nội dung của biến được khai báo bởi từ khóa Byref. Mục đích là nếu ta muốn trả về nhiều giá trị chứ không phải 01 thì ta dùng cách này. Nếu chỉ trả về 01 giá trị thì ta trả về giá trị của hàm thôi. Các bạn cứ xem ví dụ dưới đây sẽ hiểu rõ:

Mã:
Public Function GetDateFromString(ByVal sDate As String, _
                  ByRef iDay As Integer, ByRef iMonth As Integer, ByRef iYear As Integer) As Long
    On Error GoTo Loi
    Dim aDate As Date
   
    aDate = Format(sDate, "dd/mm/yyyy")
    iDay = Day(aDate)       ' Tra ve ngay cho bien iDay
    iMonth = Month(aDate)   ' Tra ve thang cho bien iMonth
    iYear = Year(aDate)     ' Tra ve nam cho bien iYear
   
    GetDateFromString = 1
    Exit Function


Loi:
    GetDateFromString = 0


End Function


Sub Test()
    Dim a As Integer, b As Integer, c As Integer
   
    If GetDateFromString("17/04/2014", a, b, c) = 1 Then
        MsgBox a & "-" & b & "-" & c                ' Khi do cac bien a,b,c da duoc ham tra ve gia tri cua nó
    End If


End Sub
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
hay quá cảm ơn ạ..............
Thực ra bài viết làm hại người đọc.

DIM là khai báo biến và KIỂU (type) của nó. ByVal/ByRef trong các sub/function là xác định CÁCH TRUYỀN tham số. KIỂU và CÁCH TRUYỀN tham số là 2 cái giầy khác nhau của một đôi giầy.

function hichic(a, b As Long)

function hichic(ByVal a, b As Long)

Trong cả 2 trường hợp KIỂU là không thay đổi. a trong cả 2 trường hợp đều có KIỂU là Variant (mặc định là Variant nếu không chỉ rõ KIỂU). b trong cả 2 trường hợp có KIỂU là Long. Cái thay đổi là CÁCH TRUYỀN tham số. Trong cả 2 trường hợp b được truyền bằng địa chỉ / tham chiếu. a trong trường hợp 1 được truyền bằng địa chỉ, trong trường hợp 2 được truyền bằng giá trị.

So sánh 2 khai báo thì KIỂU là không đổi, y như nhau. Chỉ có CÁCH TRUYỀN tham số là thay đổi.

DIM là khai báo biến cùng với KIỂU của biến. ByVal / ByRef là khai báo / xác định CÁCH TRUYỀN tham số. 2 vấn đề khác nhau mà lại so sánh với nhau?

Nếu tác giả chỉ đưa ra những ví dụ về cách truyền tham số và chỉ ra những sự khác nhau thì tôi không có gì để viết. Nhưng tác giả lại đưa cái DIM vào để so sánh thì chỉ làm rối người đọc. DIM thì liên quan gì tới cách truyền tham số? Nếu tác giả chỉ viết trên GPE thì là một chuyện, nhưng nếu viết sách mà không chính xác thì chỉ làm đầu người đọc u mê thêm . :D
 
Lần chỉnh sửa cuối:
Đọc không hiểu luôn. Cung chưa hiểu ý nghĩa thực sự của Byval và byref
 
Đọc không hiểu luôn. Cung chưa hiểu ý nghĩa thực sự của Byval và byref
Đơn giản:
Byval là ràng buộc biến số 1 chiều chỉ nhận, Byval a nếu nhập a với b là 2, gán a = 5, b sẽ có giá trị vẫn là 2 khi thủ tục gọi kết thúc.
Byref là ràng buộc biến số 2 chiều nhận và trả, Byref a nếu nhập a với b là 2, gán a = 5, b sẽ có giá trị là 5 khi thủ tục gọi kết thúc.


Mã:
Sub Test()
    Dim b
    b = 2: ProcTest1 b: debug.print b
    b = 2: ProcTest2 b: debug.print b
End Sub
Sub ProcTest1(Byval a)
    a = 5
End Sub
Sub ProcTest2(Byref a)
    a = 5
End Sub
 
Web KT
Back
Top Bottom