Hướng dẫn truyền tham số bằng giá trị (ByVal)

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,126
Được thích
24,311
Cá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.

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:

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

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 là 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.
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.

Ghi chú:
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. VB có hai từ khoá là phép đưa ra hoặc phép phủ nhận sự thay đổi toàn bộ giá trị biến, đó là ByRef và ByVal.
Mặc định VB truyền thông tin tới hàm (hoặc thủ tục) bởi tham chiếu (ByRef), đề cập 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.
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ũ.



Đây là tài liệu dịch và đã hiệu chỉnh.

Xin mời các cao thủ góp ý, bổ sung!!!
 
Bài viết của bạn rất hay cho những người mới bắt đầu làm quen với VB như mình. Bạn có thể giới thiệu cho mình các tài liệu về VB for excel được không?
Mình có tìm ở nhà sách nhưng thấy các tài liệu bán ở đó nói lan man khó hiểu quá.Cám ơn.
 
alpha1st đã viết:
Bài viết của bạn rất hay cho những người mới bắt đầu làm quen với VB như mình. Bạn có thể giới thiệu cho mình các tài liệu về VB for excel được không?
Mình có tìm ở nhà sách nhưng thấy các tài liệu bán ở đó nói lan man khó hiểu quá.Cám ơn.

Bác chịu khó tìm kỹ trên diễn đàn này rồi hãy hỏi nhen.
Coi chừng admin la đó. Anh phantuhuong có bài hướng dẫn sử dụng VBA rất hay đấy. Bạn vào phần học tập online kiếm nha.
 
Có những lúc không thể dùng ByVal để bào toàn giá trị của biến, ví dụ nếu biến là dạng MẢng
 
Có những lúc không thể dùng ByVal để bào toàn giá trị của biến, ví dụ nếu biến là dạng MẢng
Mảng bản thân nó là 1 biến dạng địa chỉ, nên nó chỉ nhận kiểu truyền là tham biến chứ không nhận là tham trị.
giải thích luôn cho bạn về cách truyền tham biến và tham tri."theo cách hiểu của mình"

nếu là truyền tham số hình thức( tham trị) thì khi gọi hàm (con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến trong hàm (con) . và biến đó được lấy dữ liệu từ chương trình chính (mẹ) đưa vào. khi kết thúc hàm(con) thì biến trong hàm (con)sẽ bị mất và nó không ảnh hưởng gì tới biến ngoài chương trình (mẹ). Chính vì vậy mọi sự thay đổi của biến trong hàm (con) sẽ không ảnh hướng đến biến ngoài

còn nếu bạn truyền tham số làm tham biến(truyền địa chỉ) thì khi gọi hàm(con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến hàm (con) nhưng biến đó nó sẽ trỏ tới địa chỉ của biến ở chương trình chính(mẹ), và từ đó mọi sự thay đổi của các biến trong hàm (con) điều tác động đến biến ngoài chương trình chính(mẹ). Tuy kết thúc hàm (con) các biến sẽ mất, nhưng vì nó đã tác động đến địa chỉ của các biến ở chương ngoài(mẹ) nên các biến của chương trình (mẹ) sẽ thay đổi
ví dụ 1 người có 2 tên Tên Tèo và tên Nam
khi mình gọi Tèo thì người ta cũng nói tôi là Tèo đầy
nhưng khi mình gọi Nam thì cũng là người đó tôi là Nam đây
(ở đây 2 tên cùng trỏ tới 1 người)
 
Lần chỉnh sửa cuối:
Ngại quá hết chỗ nói rồi hay sao ý....Nên đào mộ lên nói chuyện....--=0--=0--=0

Hay luyện Code công thần trưởng đến tầng thứ 9 thì tẩu hỏa nhập Ma ....+-+-+-++-+-+-++-+-+-+
 
không phải là đào mộ lên nói đâu, vì trên diễn đàn ít có bàn về tham trị và tham biến nên mới khơi gợi lên vậy thôi. Cái này cực kỳ quan trọng với người lập trình chuyên nghiệp, nếu bạn hiểu về nó thì kỹ thuật của bạn sẽ tăng lên đáng kể và code của bạn sẽ ngắn gọn hơn rất nhiều, nói chung nó rất quan trọng, phải khơi gợi lên cho anh em tranh luận thì mọi người sẽ hiểu rõ cội nguồn
 
không phải là đào mộ lên nói đâu, vì trên diễn đàn ít có bàn về tham trị và tham biến nên mới khơi gợi lên vậy thôi. Cái này cực kỳ quan trọng với người lập trình chuyên nghiệp, nếu bạn hiểu về nó thì kỹ thuật của bạn sẽ tăng lên đáng kể và code của bạn sẽ ngắn gọn hơn rất nhiều, nói chung nó rất quan trọng, phải khơi gợi lên cho anh em tranh luận thì mọi người sẽ hiểu rõ cội nguồn

Nói thật chứ mình cũng chả hiểu mấy về cái tham trị hay tham lam gì đó nhưng mình cũng viết code.. phà phà
Đến khi nào thật sự cần thiết (bị kẹt gì đó trong code) mà bắt buộc phải biết đến cái "tham lam", lúc đó mình sẽ nghiên cứu và tin chắc sẽ nhớ lâu hơn bất cứ bài lý thuyết nào
Ẹc... Ẹc...
 
mình phải biết đê có ai đó hỏi mình còn biết giải thích nữa anh , giải thích tại sao nó là như vậy, chính vì cái giải thích đó mà bắt buộc mình phải hiểu cặn kẽ đó anh
 
mình phải biết đê có ai đó hỏi mình còn biết giải thích nữa anh , giải thích tại sao nó là như vậy, chính vì cái giải thích đó mà bắt buộc mình phải hiểu cặn kẽ đó anh

Ờ... biết rồi! Bởi vậy chú mới làm thầy giáo, còn tôi thì luôn là.. công nhân --=0
 
sống trên đời này kiến thức thực của mình là kinh nghiệm là sự trải nghiệm, mà anh đã viết rất nhiều bài thì chắc chắn kinh nghiệm của anh rất nhiều và kiến thức của anh cũng thuộc dạng siêu đẳng, nên còn phải học anh nhiều nhiều để biết mà còn truyền lại cho học trò nữa. lúc trước chưa biết code VBA em hay đọc làm theo của anh Bate, và sau khi đã biết mặt chữ VBA như thế nào em thường xem code của anh để học hỏi, sau đó tự suy luận và chuyển đổi cái mình đã học vào VBA. vì VBA nó là cuộc sống của mình và là nồi cơm của mình rồi
 
Lần chỉnh sửa cuối:
mình phải biết đê có ai đó hỏi mình còn biết giải thích nữa anh , giải thích tại sao nó là như vậy, chính vì cái giải thích đó mà bắt buộc mình phải hiểu cặn kẽ đó anh
Vậy sao bạn không giới thiệu luôn cách khai báo byref nhưng gọi bằng byval và ngược lại?
 
Mảng bản thân nó là 1 biến dạng địa chỉ, nên nó chỉ nhận kiểu truyền là tham biến chứ không nhận là tham trị.
giải thích luôn cho bạn về cách truyền tham biến và tham tri."theo cách hiểu của mình"

nếu là truyền tham số hình thức( tham trị) thì khi gọi hàm (con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến trong hàm (con) . và biến đó được lấy dữ liệu từ chương trình chính (mẹ) đưa vào. khi kết thúc hàm(con) thì biến trong hàm (con)sẽ bị mất và nó không ảnh hưởng gì tới biến ngoài chương trình (mẹ). Chính vì vậy mọi sự thay đổi của biến trong hàm (con) sẽ không ảnh hướng đến biến ngoài

còn nếu bạn truyền tham số làm tham biến(truyền địa chỉ) thì khi gọi hàm(con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến hàm (con) nhưng biến đó nó sẽ trỏ tới địa chỉ của biến ở chương trình chính(mẹ), và từ đó mọi sự thay đổi của các biến trong hàm (con) điều tác động đến biến ngoài chương trình chính(mẹ). Tuy kết thúc hàm (con) các biến sẽ mất, nhưng vì nó đã tác động đến địa chỉ của các biến ở chương ngoài(mẹ) nên các biến của chương trình (mẹ) sẽ thay đổi
ví dụ 1 người có 2 tên Tên Tèo và tên Nam
khi mình gọi Tèo thì người ta cũng nói tôi là Tèo đầy
nhưng khi mình gọi Nam thì cũng là người đó tôi là Nam đây
(ở đây 2 tên cùng trỏ tới 1 người)

Với bài này:

Có những lúc không thể dùng ByVal để bào toàn giá trị của biến, ví dụ nếu biến là dạng MẢng

Tôi nghĩ PMXD muốn nói rằng, trong khi khai báo tham số trong Sub hay Function, thì biến mảng không thể dùng ByVal đển khai báo biến được.

Chẳng hạn:

Mã:
Sub Test([COLOR=#ff0000][B]ByRef [/B][/COLOR]OneArray[COLOR=#ff0000][B]()[/B][/COLOR])

Bạn không thể nào thay ByRef bằng ByVal được, hoặc bạn không dùng gì cả (ngầm hiểu là ByRef).

Mã:
Sub Test(OneArray[COLOR=#FF0000][B]()[/B][/COLOR])
 
Với bài này:



Tôi nghĩ PMXD muốn nói rằng, trong khi khai báo tham số trong Sub hay Function, thì biến mảng không thể dùng ByVal đển khai báo biến được.

Chẳng hạn:

Mã:
Sub Test([COLOR=#ff0000][B]ByRef [/B][/COLOR]OneArray[COLOR=#ff0000][B]()[/B][/COLOR])

Bạn không thể nào thay ByRef bằng ByVal được, hoặc bạn không dùng gì cả (ngầm hiểu là ByRef).

Mã:
Sub Test(OneArray[COLOR=#FF0000][B]()[/B][/COLOR])

Ờ... Cái này thì gọi là kinh nghiệm nè! Bảo giải thích tận gốc thì.. ứ biết nhưng mà luôn hiểu cách "vận hành" rằng "nó phải là vậy!"
Ẹc... Ẹc...
 
Thấy Bạn phihndhsp nói Tham số, tham trị hay tham biến gì đó .... mình là người tự mò mẫn code trên GPE hoc tập nên cũng không hiểu lắm... có phải là kiểu sub1 gọi sub2 chạy hay không vậy...VD như code sau mình viết là tham số hay là tham gì ..??? vậy thú thật là mình biết viết thôi còn hỏi nó là cái gì thì mình hỏng biết ...mong Bạn cùng các thành Viên GPE chỉ thêm và cách viết như vậy có ổn áp ko nha
PHP:
Public Sub DataBan(Nguon As Range, Dich As Range)
    Dim arr(), kq(), i&, j&, k&
    arr = Nguon.Value
    ReDim kq(1 To UBound(arr, 1), 1 To UBound(arr, 2))
    For i = 1 To UBound(arr, 1)
        If arr(i, 3) <> "" Then
            k = k + 1
            For j = 1 To UBound(arr, 2)
                kq(k, j) = arr(i, j)
            Next
        End If
    Next
    Dich.Resize(UBound(arr, 1), UBound(arr, 2)) = kq
End Sub

Sub Main
PHP:
Public Sub Main()
     DataBan Range("A1:D20"), [H1]                      ''<- Xuat Kq ra ko noi duoi nha
     DataBan Range("A1:D20"), Range("M65536").End(3)(2) ''<- Xuat KQ ra noi duoi nhau
End Sub
 
Lần chỉnh sửa cuối:
Code của bạn thay đổi cái chứa trong object range chứ không phải biến range. Vì vậy bạn không thể dùng nó để kết luận rằng mình đang dùng tham trị hay tham chiếu.

Muốn thử thì bạn thử dùng lệnh Set Nguon = abc, hay Set Dich = def bên trong Sub. Sau đó dùng Address để kiểm lại xem. Nếu chúng không thay đổi thì kết luận là byVal.
 
Mảng bản thân nó là 1 biến dạng địa chỉ, nên nó chỉ nhận kiểu truyền là tham biến chứ không nhận là tham trị.
giải thích luôn cho bạn về cách truyền tham biến và tham tri."theo cách hiểu của mình"

nếu là truyền tham số hình thức( tham trị) thì khi gọi hàm (con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến trong hàm (con) . và biến đó được lấy dữ liệu từ chương trình chính (mẹ) đưa vào. khi kết thúc hàm(con) thì biến trong hàm (con)sẽ bị mất và nó không ảnh hưởng gì tới biến ngoài chương trình (mẹ). Chính vì vậy mọi sự thay đổi của biến trong hàm (con) sẽ không ảnh hướng đến biến ngoài

còn nếu bạn truyền tham số làm tham biến(truyền địa chỉ) thì khi gọi hàm(con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến hàm (con) nhưng biến đó nó sẽ trỏ tới địa chỉ của biến ở chương trình chính(mẹ), và từ đó mọi sự thay đổi của các biến trong hàm (con) điều tác động đến biến ngoài chương trình chính(mẹ). Tuy kết thúc hàm (con) các biến sẽ mất, nhưng vì nó đã tác động đến địa chỉ của các biến ở chương ngoài(mẹ) nên các biến của chương trình (mẹ) sẽ thay đổi
Điều này không phải lúc nào cũng đúng. Đối với các kiểu đơn giản như Long, Interger... thì khai báo ByVal sẽ không thay đổi nhưng đối với các kiểu phức tạp thì chưa chắc. Ta hay thấy các API tham số toàn ByVal nhưng vẫn trả về hàng loạt thông tin thông qua các tham số. Khi tham số kiểu object được khai báo ByVal thì các thuộc tính của object đó vẫn có thể thay đổi chỉ có object đó không trỏ đến vị trí khác dù trong hàm có lệnh Set để thay đổi object. Chẳng hạn code sau:
Mã:
Sub ABC(ByVal r As Range)
   r.Value = r.Value + 1
   Set r = r.Offset(1, 1)
   r.Value = r.Value + 2
End Sub
Sub Test()
    Dim r As Range
    Set r = Range("A1")
    r.Value = 1
    MsgBox r.Address
    MsgBox r.Value
    ABC r
    MsgBox r.Address
    MsgBox r.Value
End Sub
Code này khai báo ByVal tham số kiểu Range, sau khi gọi hàm thì địa chỉ của Range không đổi nhưng Value của Range thì lại thay đổi. Điều này xảy ra vì VB quản lý object (ở đây là range) qua địa chỉ nên khai báo byval sẽ tạo 1 bản sao của địa chỉ chứ không phải bản sao của toàn bộ object.
 
Nếu để hiểu rõ cách truyền tham biến cho các biến đối tượng anh nên xem các sách lập trình hướng đối tượng. Và sau khi xem xong thì anh sẽ biết tại sao biến đối tượng lại set.
 
Có những lúc không thể dùng ByVal để bào toàn giá trị của biến, ví dụ nếu biến là dạng MẢng

Mảng bản thân nó là 1 biến dạng địa chỉ, nên nó chỉ nhận kiểu truyền là tham biến chứ không nhận là tham trị.
giải thích luôn cho bạn về cách truyền tham biến và tham tri."theo cách hiểu của mình"

nếu là truyền tham số hình thức( tham trị) thì khi gọi hàm (con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến trong hàm (con) . và biến đó được lấy dữ liệu từ chương trình chính (mẹ) đưa vào. khi kết thúc hàm(con) thì biến trong hàm (con)sẽ bị mất và nó không ảnh hưởng gì tới biến ngoài chương trình (mẹ). Chính vì vậy mọi sự thay đổi của biến trong hàm (con) sẽ không ảnh hướng đến biến ngoài

còn nếu bạn truyền tham số làm tham biến(truyền địa chỉ) thì khi gọi hàm(con) bộ nhớ sẽ cấp phát 1 vùng nhớ nào đó cho biến hàm (con) nhưng biến đó nó sẽ trỏ tới địa chỉ của biến ở chương trình chính(mẹ), và từ đó mọi sự thay đổi của các biến trong hàm (con) điều tác động đến biến ngoài chương trình chính(mẹ). Tuy kết thúc hàm (con) các biến sẽ mất, nhưng vì nó đã tác động đến địa chỉ của các biến ở chương ngoài(mẹ) nên các biến của chương trình (mẹ) sẽ thay đổi
ví dụ 1 người có 2 tên Tên Tèo và tên Nam
khi mình gọi Tèo thì người ta cũng nói tôi là Tèo đầy
nhưng khi mình gọi Nam thì cũng là người đó tôi là Nam đây
(ở đây 2 tên cùng trỏ tới 1 người)

Câu gảii thích trên chỉ nói lý thuyết chứ không đụng đến thực tế của tiền đề.

Trên thực tế, muốn bảo vệ mảng cũng được. Nhưng phải "che mắt" VBA bằng cách chuyển dạng tham.
Trong VBA, cách chuyển dạng dễ nhất là dùng loại dạng tổng quát nhất, tức là Variant.

Đổi Sub x(a()) thành Sub x(ByVal a) hay Sub x(ByVal a As Variant). Lưu ý: nhưng không phải Sub x(a), vì cái này cũng là ByRef

Trong câu Sub x(a()), compiler chỉ chép lại địa chỉ của mảng a
Trong câu Sub x(ByVal a), compiler chép lại nguyên mảng để dùng bên trong Sub. Đương nhiên cách này rất tốn năng lượng.
 
Nếu để hiểu rõ cách truyền tham biến cho các biến đối tượng anh nên xem các sách lập trình hướng đối tượng. Và sau khi xem xong thì anh sẽ biết tại sao biến đối tượng lại set.
Nếu được đề nghị bạn cho một VD điển hình coi....chứ lý thuyết suông chán lắm và lại tư duy triều tượng nữa chứ chắc cái đầu ngắn học của mình nổ tung mất //**/
 
Web KT
Back
Top Bottom