2009-10-08 30 views
28

Tôi đang đối mặt với điều gì đó lạ trong VBScript. Khi viết một thủ tục mà tôi muốn tham số được chuyển qua tham chiếu, cách gọi thủ tục này thay đổi cách thông số được truyền!ByRef và ByVal trong VBScript

Dưới đây là một ví dụ:

Sub IncrementByRef(ByRef Value) 
    Value = Value + 1 
End Sub 

Sub IncrementByVal(ByVal Value) 
    Value = Value + 1 
End Sub 

Dim Num 
Num = 10 
WScript.Echo "Num : " & Num 
IncrementByRef(Num) : WScript.Echo "IncrementByRef(Num) : " & Num 
IncrementByRef Num : WScript.Echo "IncrementByRef Num : " & Num 
IncrementByVal(Num) : WScript.Echo "IncrementByVal(Num) : " & Num 
IncrementByVal Num : WScript.Echo "IncrementByVal Num : " & Num 

Và đây là kết quả:

U:\>cscript //nologo byrefbyval.vbs 
Num : 10 
IncrementByRef(Num) : 10 
IncrementByRef Num : 11 
IncrementByVal(Num) : 11 
IncrementByVal Num : 11 

U:\> 

Khi xác định các thông số được truyền ByVal, nó hoạt động như mong đợi, bất kể đường đi thủ tục là gọi là. Nhưng khi xác định các thông số được thông qua ByRef, nó sẽ làm việc như mong đợi nếu gọi các thủ tục theo cách này:

IncrementByRef Num 

nhưng không phải theo cách này:

IncrementByRef(Num) 

Điều này có vẻ lạ với tôi. Có cách nào để đảm bảo các tham số được truyền qua ByRef, không cần biết thủ tục được gọi như thế nào?

Trả lời

38

Eric Lippert có một bài viết tuyệt vời về việc sử dụng dấu ngoặc đơn trong VBScript: What do you mean "cannot use parentheses?" dụ bạn minh họa một trong những điểm ông đề cập đến, đó là: kèm theo một cuộc tranh luận ByRef trong ngoặc vượt qua nó như ByVal.

Trong ngắn hạn, dấu ngoặc đơn trong các cuộc gọi chương trình con VBScript có thể được đặt không chỉ xung quanh danh sách đối số, mà còn xung quanh các đối số riêng lẻ (trong trường hợp đó chúng bị buộc ByVal). Và VBScript chỉ hy vọng danh sách đối số được đặt trong dấu ngoặc đơn nếu từ khóa Call được sử dụng. Vì cuộc gọi IncrementByRef(Num) không sử dụng từ khóa Call, VBScript xử lý dấu ngoặc đơn như được áp dụng cho đối số chương trình con và do đó chuyển nó ByVal thay vì ByRef.

Khó hiểu, nhưng đó là cách hoạt động của nó.

+0

... Vì vậy, đơn giản? Và tôi đã được strugling cho nó cho một giờ! Cảm ơn, +1. – Jet

18

Đó là một tính năng, không phải là một lỗi:
http://msdn.microsoft.com/en-us/library/ee478101.aspx

tham số Một ByRef được truyền theo giá trị nếu đối số được kèm theo trong ngoặc đơn và dấu ngoặc đơn không áp dụng cho danh sách đối số.

Dấu ngoặc áp dụng cho danh sách đối số nếu một trong những điều sau đây là đúng:

  • Tuyên bố là lời kêu gọi hàm có chuyển nhượng với giá trị trả lại.

  • Tuyên bố sử dụng từ khóa Cuộc gọi. (Các từ khóa Gọi tùy chọn có thể được sử dụng cho một gọi chương trình con, hoặc cho một cuộc gọi chức năng mà không cần chuyển nhượng.)

Vì vậy, hãy thử sử dụng các từ khóa Gọi hoặc có nó trả về một giá trị.

+6

Đó là một trong những chủ đề MSDN khó hiểu nhất liên quan đến VBScript mà tôi từng thấy –

7

Để rõ ràng. Dấu ngoặc đơn có ba mục đích khác nhau.

  1. Được sử dụng để gửi kèm theo một danh sách đối số khi xác định hoặc gọi thủ tục
  2. Để chỉ ra một indexer trên một mảng.
  3. Là một toán tử trong một biểu thức.

Có hai cách để gọi thủ tục dưới dạng tuyên bố hoặc dưới dạng biểu thức.

Biểu hiện: -

x = func(y) 

Bản Tuyên Bố: -

func y 

Lưu ý từ khóa Call gọi các thủ tục như thể nó là một phần của một biểu thức do đó danh sách đối số phải được chứa trong parantheses.

Ở trên, chính bản thân số y thể hiện sự đơn giản. Chúng tôi cũng có thể đã sử dụng y + z tại thời điểm này. Trong thực tế, chúng ta có thể sử dụng bất kỳ biểu thức hợp lệ nào tại thời điểm này, bao gồm một biểu thức sử dụng toán tử dấu ngoặc đơn. Ví dụ: -

x = (y) 

là một biểu thức hợp lệ. Do đó khi bạn làm: -

func(y) 

VBScript thấy cuộc gọi đến func mà kết quả của biểu thức (y) được thông qua. Bây giờ ngay cả khi func xác định thông số này là ByRef giá trị trong y sẽ không bị ảnh hưởng bởi vì y không thực sự được truyền dưới dạng tham số. Những gì đã được thông qua là kết quả của biểu thức (y) mà sẽ được lưu trữ ở đâu đó tạm thời. Ngay cả khi cửa hàng tạm thời này được sửa đổi bởi func nó sẽ bị loại bỏ sau đó và do đó có cùng hành vi với thông số được đánh dấu ByVal.

2
IncrementByRef Num 

cuộc gọi và gia tăng sử dụng một tham chiếu đến Num

IncrementByRef (47 + 3) 

cuộc gọi và gia tăng sử dụng một tham chiếu đến "50". Mà bị loại bỏ khi thoát.

IncrementByRef (Num) 
IncrementByRef (Num + 18)*5 
IncrementByRef Cint("32") 

Tất cả đều hợp pháp trong BASIC, giống như trong FORTRAN. Nổi tiếng, trong một FORTRAN sớm, đi ngang qua ref cho phép bạn thay đổi giá trị của biểu thức như

5 

nào là đủ khó hiểu mà chỉ rất nhỏ, trình biên dịch FORTRAN đầu có mà loại hành vi.

-1

Thật đơn giản.Khi bạn tạo một hàm hoặc phụ và bạn có thể gọi chúng bằng những cách:

Đối với không có giá trị trả về:

myFunction "This is a reference" 

Đối với giá trị trả về:

myValue = myFunction ("This is a reference") 
0

Tôi không chắc tôi làm theo câu hỏi hoặc câu trả lời, nhưng tôi sẽ chia sẻ câu hỏi này.

Bất kể bạn có một thường trình con chức năng hay không, xác định tham số được truyền trong ByVal hoặc ByRef sẽ xác định xem giá trị của tham số có giữ lại giá trị của nó ngoài cuộc gọi phụ hay hàm. Nếu tôi có chức năng sau:

Function ThisFByRef(ByRef MyValue) 
End Function 

Bất kỳ điều gì tôi làm cho tham số trong hàm (hoặc thường trình con) sẽ giữ lại giá trị sau khi hoàn thành chức năng. ByValByRef được liên kết với phạm vi của tiểu thường trình hoặc chức năng. Bất kỳ tham số nào được thông qua ByVal sẽ không giữ lại các thay đổi xảy ra trong hàm hoặc thường trình con được gọi. Ngoài ra, bất kỳ tham số nào được thông qua ByRef sẽ giữ lại giá trị mà thông số đã được thay đổi thành bên trong thường trình hoặc chức năng phụ. Việc trả về một giá trị chỉ có thể được thực hiện với một hàm và không phải là một tuyến con và không ảnh hưởng đến giá trị của tham số được truyền vào trừ khi tham số được chuyển qua ByRef và thay đổi trong hàm. Ví dụ:

Dim x 
Dim ThisFValue 

x = 0 
ThisFValue = ThisFByRef(x) 
At this point the values would be: 
ThisFValue = 2 
x = 1 

x = 0 
ThisFValue = ThisFByVal(x) 
At this point the values would be: 
ThisFValue = 2 
x = 0 

Function ThisFByRef(ByRef x) 
    x = x + 1 
    ThisFByRef = x + 1 
End Function 

Function ThisFByVal(ByVal x) 
    x = x + 1 
    ThisFByVal = x + 1 
End Function 
+1

IMHO, câu trả lời của bạn không làm rõ điểm của tôi mà tôi thấy lạ sau khi gọi 'ThisFValue = ThisByRef (x)', x chứa 1, nhưng nếu tôi gọi nó như thế này 'ThisByRef (x)', x chứa 0. chức năng, cùng một tham số, nhưng kết quả khác nhau! –

Các vấn đề liên quan