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

Liên hệ QC

Người dùng đang xem chủ đề này

PhanTuHuong

VBA & VB.NET for Excel & AutoCad
Thành viên danh dự
Tham gia
13/6/06
Bài viết
7,216
Được thích
24,736
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 //**/
 
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.
Tôi không cần biết tại sao biến đối tượng lại set nhưng tôi biết khi nào phải dùng từ khóa set (dựa vào kinh nghiệm chứ cũng chả phải đọc sách, sách lập trình tôi chưa đọc qua cuốn nào). Với tôi (và tôi nghĩ đa số thành viên của GPE) như thế là đủ.
Cá nhân tôi là người viết code thực dụng nên nếu có đọc tài liệu tôi cũng tìm nhưng tài liệu kiểu "Làm như thế nào" chứ tôi sẽ không đọc những tài liệu giải thích "Vì sao phải làm như vậy".
 
Đây là nội dung tôi dịch lại từ sách, nhiều nội dung còn hạn chế. Khi các bạn áp dụng sẽ thấy rõ, do đó cần mỗi lần xuất bản sách Lập trình VBA, tôi đều update. Có những nội dung viết lại hoặc sử dụng ví dụ khác hoàn toàn.
 
Đây là nội dung tôi dịch lại từ sách, nhiều nội dung còn hạn chế. Khi các bạn áp dụng sẽ thấy rõ, do đó cần mỗi lần xuất bản sách Lập trình VBA, tôi đều update. Có những nội dung viết lại hoặc sử dụng ví dụ khác hoàn toàn.
Nội dung mình có thấy đâu ... hay Bạn quên Úp
 
Bạn đọc lại bài đầu nhé.
Như em đã nói ở bài 17, nếu biến là object hoặc kiểu tự tạo thì VB quản lý những biến này thông qua địa chỉ vùng nhớ của biến. Nếu gọi hàm và truyền tham số object theo kiểu ByVal thì các thuộc tính object đó vẫn thay đổi được, chỉ có địa chỉ của biến không đổi (ví dụ ở bài 17).
Còn để ép 1 hàm đã khai báo ByRef gọi kiểu ByVal thì đưa tham số vào trong ngoặc đơn. Ví dụ:
Mã:
Sub ABC(ByRef x As Long)
      x=x+1
End Sub
Sub Main()
      Dim x As Long
      x=1
      ABC x
      Msgbox x
      x=1
      ABC(x)
      Msgbox x
End Sub
Code trên, lần đầu gọi Sub, biến x là ByRef nên thay đổi thành 2; lần sau x gọi theo kiểu ByVal nên vẫn =1.
Còn hàm khai báo kiểu ByVal, gọi kiểu ByRef thì em chưa biết, hay không thể?
 
Như em đã nói ở bài 17, nếu biến là object hoặc kiểu tự tạo thì VB quản lý những biến này thông qua địa chỉ vùng nhớ của biến. Nếu gọi hàm và truyền tham số object theo kiểu ByVal thì các thuộc tính object đó vẫn thay đổi được, chỉ có địa chỉ của biến không đổi (ví dụ ở bài 17).
Còn để ép 1 hàm đã khai báo ByRef gọi kiểu ByVal thì đưa tham số vào trong ngoặc đơn. Ví dụ:
Mã:
Sub ABC(ByRef x As Long)
      x=x+1
End Sub
Sub Main()
      Dim x As Long
      x=1
      ABC x
      Msgbox x
      x=1
      ABC(x)
      Msgbox x
End Sub
Code trên, lần đầu gọi Sub, biến x là ByRef nên thay đổi thành 2; lần sau x gọi theo kiểu ByVal nên vẫn =1.
Còn hàm khai báo kiểu ByVal, gọi kiểu ByRef thì em chưa biết, hay không thể?
Tôi nghĩ ví dụ của bạn không phải là khai báo kiểu ByRef gọi kiểu ByVal

Ở lần gọi thủ tục ABC thứ 2, tham số đưa vào thủ tục là giá trị của biểu thức (x) chứ không phải là biến x, nên không có gì ảnh hưởng đến biến x cả.
Tôi thí nghiệm như sau:
PHP:
Private Sub Main(ByRef Bien As Variant)
      MsgBox TypeName(Bien)
End Sub
PHP:
Sub Test1()
      Dim Rng As Range
      ActiveCell.Value = "GPE"
      Set Rng = ActiveCell
      Main Rng
End Sub
PHP:
Sub Test2()
      Dim Rng As Range
      ActiveCell.Value = "GPE"
      Set Rng = ActiveCell
      Main (Rng)
End Sub
 
Tôi nghĩ ví dụ của bạn không phải là khai báo kiểu ByRef gọi kiểu ByVal

Ở lần gọi thủ tục ABC thứ 2, tham số đưa vào thủ tục là giá trị của biểu thức (x) chứ không phải là biến x, nên không có gì ảnh hưởng đến biến x cả.
Tôi thí nghiệm như sau:
PHP:
Private Sub Main(ByRef Bien As Variant)
      MsgBox TypeName(Bien)
End Sub
PHP:
Sub Test1()
      Dim Rng As Range
      ActiveCell.Value = "GPE"
      Set Rng = ActiveCell
      Main Rng
End Sub
PHP:
Sub Test2()
      Dim Rng As Range
      ActiveCell.Value = "GPE"
      Set Rng = ActiveCell
      Main (Rng)
End Sub
Mình nói ở bài trên là theo MS https://msdn.microsoft.com/en-us/library/chy4288y.aspx chứ mình không bịa ra.
Trường hợp này đúng là như vậy, đây chắc lại liên quan đến call ByVal hay ByRef với object.
 
Vậy là bắt buộc phải có ByVal vào .....vậy thì bài #15 MÌNH viết cho thêm Byval vào cũng vậy mà bỏ đi cũng vậy ...Mình chưa tìm thấy sự khác biệt ...Quả thiệt là +-+-+-++-+-+-+ đây
cảm ơn Bạn
Dưới đây mà một ví dụ mà bạn phải khai báo Byval, nếu bỏ trống hoặc khai báo Byref thì sẽ không ra kết quả.

Dữ liệu:
Tôi có một bảng tổng hợp, và nhiều bảng chi tiết.
Trên bảng tổng hợp có 2 cột Mã và Số lượng, cột Mã là danh sách duy nhất các mã, cột Số lượng là số lượng tương ứng của mã
Trên mỗi bảng chi tiết cũng có 2 cột Mã và Số lượng nhưng cột Mã không phải là danh sách duy nhất mà có lặp lại.

Yêu cầu:
Xác định dữ liệu trên bảng tổng hợp là được tổng hợp từ bảng chi tiết nào

Thuật toán:
Tôi làm như sau:
- Gán dữ liệu bảng tổng hợp vào một mảng (mảng tổng hợp)
- Duyệt qua các bảng dữ liệu chi tiết
- Dùng một hàm phụ
+ Duyệt qua các dòng trong mảng chi tiết và trừ số lượng của mã tương ứng trên bảng tổng hợp
+ Sau khi duyệt qua hết các dòng của mảng chi tiết thì duyệt qua các dòng của mảng tổng hợp, nếu tất cả các dòng của mảng tổng hợp đều có số lượng bằng 0 thì mảng tổng hợp được tổng hợp từ mảng chi tiết đang xét.

Code:
PHP:
Sub Sub_Chinh()
Dim Dic As Object, TongHop As Variant, i As Long, Rng As Range
Set Dic = CreateObject("Scripting.Dictionary")
TongHop = Range(ThisWorkbook.Sheets("TongHop").[B2], ThisWorkbook.Sheets("TongHop").[A65536].End(xlUp)).Value
For i = 1 To UBound(TongHop, 1)
    Dic.Item(TongHop(i, 1)) = i
Next
ThisWorkbook.Sheets("TongHop").[E2:F100].ClearContents
For i = 1 To ThisWorkbook.Sheets.Count
    If ThisWorkbook.Sheets(i).Name <> "TongHop" Then
        Set Rng = Range(ThisWorkbook.Sheets(i).[B2], ThisWorkbook.Sheets(i).[A65536].End(xlUp))
        If Sub_Con(TongHop, Rng.Value, Dic) Then
            ThisWorkbook.Sheets("TongHop").[E2].Resize(Rng.Rows.Count, 2).Value = Rng.Value
            Exit Sub
        End If
    End If
Next
End Sub
PHP:
Private Function Sub_Con(ByVal TongHop As Variant, ChiTiet As Variant, Dic As Object) As Boolean
Dim i As Long, Index As Long
For i = 1 To UBound(ChiTiet, 1)
    If Dic.Exists(ChiTiet(i, 1)) Then
        Index = CLng(Dic.Item(ChiTiet(i, 1)))
        TongHop(Index, 2) = TongHop(Index, 2) - ChiTiet(i, 2)
    Else
        Sub_Con = False
        Exit Function
    End If
Next
Sub_Con = True
For i = 1 To UBound(TongHop, 1)
    If TongHop(i, 2) <> 0 Then
        Sub_Con = False
        Exit Function
    End If
Next
End Function
Lưu ý:
Nếu bạn không khai báo Byval cho tham số TongHop của Sub_Con thì sẽ không có kết quả.
Đương nhiên bạn có thể dùng cách khác hoặc vẫn là cách này nhưng trước khi gọi Sub_Con bạn gán TongHop vào một biết khác và dùng biến này để truyền cho Sub_Con thì không có gì để nói nữa vì nó không thuộc phạm vi của vấn đề đang đề cập nữa.
 

File đính kèm

Dưới đây mà một ví dụ mà bạn phải khai báo Byval, nếu bỏ trống hoặc khai báo Byref thì sẽ không ra kết quả.

Dữ liệu:
Tôi có một bảng tổng hợp, và nhiều bảng chi tiết.
Trên bảng tổng hợp có 2 cột Mã và Số lượng, cột Mã là danh sách duy nhất các mã, cột Số lượng là số lượng tương ứng của mã
Trên mỗi bảng chi tiết cũng có 2 cột Mã và Số lượng nhưng cột Mã không phải là danh sách duy nhất mà có lặp lại.

Yêu cầu:
Xác định dữ liệu trên bảng tổng hợp là được tổng hợp từ bảng chi tiết nào

Thuật toán:
Tôi làm như sau:
- Gán dữ liệu bảng tổng hợp vào một mảng (mảng tổng hợp)
- Duyệt qua các bảng dữ liệu chi tiết
- Dùng một hàm phụ
+ Duyệt qua các dòng trong mảng chi tiết và trừ số lượng của mã tương ứng trên bảng tổng hợp
+ Sau khi duyệt qua hết các dòng của mảng chi tiết thì duyệt qua các dòng của mảng tổng hợp, nếu tất cả các dòng của mảng tổng hợp đều có số lượng bằng 0 thì mảng tổng hợp được tổng hợp từ mảng chi tiết đang xét.

Code:
PHP:
Sub Sub_Chinh()
Dim Dic As Object, TongHop As Variant, i As Long, Rng As Range
Set Dic = CreateObject("Scripting.Dictionary")
TongHop = Range(ThisWorkbook.Sheets("TongHop").[B2], ThisWorkbook.Sheets("TongHop").[A65536].End(xlUp)).Value
For i = 1 To UBound(TongHop, 1)
    Dic.Item(TongHop(i, 1)) = i
Next
ThisWorkbook.Sheets("TongHop").[E2:F100].ClearContents
For i = 1 To ThisWorkbook.Sheets.Count
    If ThisWorkbook.Sheets(i).Name <> "TongHop" Then
        Set Rng = Range(ThisWorkbook.Sheets(i).[B2], ThisWorkbook.Sheets(i).[A65536].End(xlUp))
        If Sub_Con(TongHop, Rng.Value, Dic) Then
            ThisWorkbook.Sheets("TongHop").[E2].Resize(Rng.Rows.Count, 2).Value = Rng.Value
            Exit Sub
        End If
    End If
Next
End Sub
PHP:
Private Function Sub_Con(ByVal TongHop As Variant, ChiTiet As Variant, Dic As Object) As Boolean
Dim i As Long, Index As Long
For i = 1 To UBound(ChiTiet, 1)
    If Dic.Exists(ChiTiet(i, 1)) Then
        Index = CLng(Dic.Item(ChiTiet(i, 1)))
        TongHop(Index, 2) = TongHop(Index, 2) - ChiTiet(i, 2)
    Else
        Sub_Con = False
        Exit Function
    End If
Next
Sub_Con = True
For i = 1 To UBound(TongHop, 1)
    If TongHop(i, 2) <> 0 Then
        Sub_Con = False
        Exit Function
    End If
Next
End Function
Lưu ý:
Nếu bạn không khai báo Byval cho tham số TongHop của Sub_Con thì sẽ không có kết quả.
Đương nhiên bạn có thể dùng cách khác hoặc vẫn là cách này nhưng trước khi gọi Sub_Con bạn gán TongHop vào một biết khác và dùng biến này để truyền cho Sub_Con thì không có gì để nói nữa vì nó không thuộc phạm vi của vấn đề đang đề cập nữa.
đúng là vậy. cảm ơn Bạn giải thích rất chi tiết
 
Dưới đây mà một ví dụ mà bạn phải khai báo Byval, nếu bỏ trống hoặc khai báo Byref thì sẽ không ra kết quả.

….
Lưu ý:
Nếu bạn không khai báo Byval cho tham số TongHop của Sub_Con thì sẽ không có kết quả.
Đương nhiên bạn có thể dùng cách khác hoặc vẫn là cách này nhưng trước khi gọi Sub_Con bạn gán TongHop vào một biết khác và dùng biến này để truyền cho Sub_Con thì không có gì để nói nữa vì nó không thuộc phạm vi của vấn đề đang đề cập nữa.


Code của bạn dùng Variant để tổng quát hóa kiểu dữ liệu của tham. (tôi đã có đề cập đến kỹ thuật này rồi)

Variant là một kiểu tổng quát. Nó là một loại mà tiếng nhà nghề gọi là supertype, có thể biến theo đúng dạng nào cũng được, tùy theo đối tượng mà nó được gán. Khi trình dịch (compiler) gặp loại biến này trên tham số khai báo, nó tự biết rằng tham truyền vào có thể rất phức tạp cho nên nó để hở (vì kiểu phức tạp nên mặc định là ByRef). Để tránh sự mặc định này, bạn bắt buộc cho trình dịch biết thẳng là ByVal.

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


Trừ phi bạn muốn học lập trình chuyên nghiệp, chứ bỏ công học lập trình hướng đối tượng không hẳn là hiệu quả đâu. Tôi không chắc là một người bỏ ra học một năm HĐT có thể trả lời được câu này.

Nguyên tắc mà bạn cần biết là trình dịch (compiler) duyệt một câu lệnh, nó bắt buộc phải dịch một cách thống nhất, không chấp nhận chuyện hiểu lầm.

Lý do là vì ký tự = được dùng theo nhiều nghĩa:
Lệnh gán căn bản của BASIC là lệnh LET. Về sau này, các phiên bản BASIC đều bỏ từ LET, compiler (trình dịch) phải tự động duyệt cả các trường hợp dấu = để hiểu đó là lệnh gán hay là phép so sánh. Điều này complier có thể làm được dễ dàng đối với các biến dạng căn bản (integer, string,…)
Tuy nhiên khi áp dụng với đối tượng, các đối tượng trong VBA có hàm/thuộc tính ngầm để đáp ứng với nhiều loại gán cho nên lệnh gán thông thường có thể gây hiểu lầm. Ví dụ bạn có:
Dim a As Variant
a = Range("a1:a2")
Giả sử VBA không có từ khóa SET thì Compiler sẽ hiểu như thế nào đây? a bây giờ là range hay mảng?
Từ khóa Set giúp cho trình dịch giải quyết sự thắc mắc này.
Chạy thử code này sẽ rõ:
Sub t()
Dim a As Variant
Set a = Range("a1:a2")
Debug.Print TypeName(a)
a = Range("a1:a2")
Debug.Print TypeName(a)
End Sub
Kết quả cho thấy là

  1. Range
  2. Variant()
 

Bài viết mới nhất

Back
Top Bottom