疑難排解程序 (Visual Basic)
此頁面列出使用程序時可能發生的一些常見問題。
從函式程序傳回陣列型別
如果Function
程序傳回陣列資料類型,則您無法使用Function
名稱將值儲存在陣列的元素中。 如果您嘗試這麼做,編譯器會將它解譯為對Function
的呼叫。 下列範例會產生編譯器錯誤:
Function AllOnes(n As Integer) As Integer()
For i As Integer = 1 To n - 1
' The following statement generates a COMPILER ERROR.
AllOnes(i) = 1
Next
' The following statement generates a COMPILER ERROR.
Return AllOnes()
End Function
陳述式AllOnes(i) = 1
會產生編譯器錯誤,因為它似乎以錯誤的資料類型引數呼叫 AllOnes
(純量Integer
而非Integer
陣列)。 陳述式 Return AllOnes()
會產生編譯器錯誤,因為它似乎不以引數呼叫 AllOnes
。
正確方法: 若要能夠對於要傳回的陣列修改陣列的元素,請將內部陣列定義為區域變數。 下列範例會編譯而不發生錯誤:
Function AllOnes(n As Integer) As Integer()
Dim iArray(n - 1) As Integer
For i = 0 To n - 1
iArray(i) = 1
Next
Return iArray
End Function
程序呼叫未修改的引數
如果您想要允許程序變更呼叫程序碼中引數基礎的程序設計元素,您必須以傳址方式傳遞該元素。 但是,即使您以傳值方式傳遞參考型別引數,程序還是可以存取參考型別引數的元素。
基礎變數。 若要允許程序取代基礎變數元素本身的值,程序必須宣告 ByRef 參數。 此外,呼叫端程式碼不得以括弧括住引數,因為這會覆寫
ByRef
傳遞機制。參考型別元素。 如果您宣告參數 ByVal,程序就無法修改基礎變數元素本身。 不過,如果引數是參考型別,程序可以修改指向的物件成員,即使無法取代變數的值也一樣。 例如,如果引數是陣列變數,程序就無法為其指派新的陣列,但可以變更其中一個或多個元素。 已變更的元素會反映在呼叫程式碼的基礎陣列變數中。
下列範例會定義兩個程序,這些程序會依值採用陣列變數,並在其元素上運作。 程序 increase
只會將一個陣列新增至每個元素。 程序 replace
會將新的陣列指派給 參數 a()
,然後將一個陣列新增至每個元素。 不過,重新指派不會影響呼叫程式碼中的基礎陣列變數,因為 a()
已宣告 ByVal
。
Public Sub increase(ByVal a() As Long)
For j As Integer = 0 To UBound(a)
a(j) = a(j) + 1
Next j
End Sub
Public Sub replace(ByVal a() As Long)
Dim k() As Long = {100, 200, 300}
a = k
For j As Integer = 0 To UBound(a)
a(j) = a(j) + 1
Next j
End Sub
下列範例會呼叫 increase
和 replace
:
Dim n() As Long = {10, 20, 30, 40}
Call increase(n)
MsgBox("After increase(n): " & CStr(n(0)) & ", " &
CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))
Call replace(n)
MsgBox("After replace(n): " & CStr(n(0)) & ", " &
CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))
第一個 MsgBox
呼叫會顯示「增加 (n) 之後:11、21、31、41」。 因為 n
是參考型別,所以即使傳遞 ByVal
,increase
也可以變更其中的成員。
第二個 MsgBox
呼叫會顯示「取代 (n) 之後:11、21、31、41」。 因為 n
已傳遞 ByVal
,所以 replace
無法指派新的陣列來修改變數 n
。 replace
建立新的陣列執行個體 k
並將它指派給區域變數a
時,它會失去呼叫程式碼對於n
傳入的參考。 它遞增a
的成員時,只會影響本地陣列k
。
正確方法: 若要能夠修改基礎變數元素本身,請以傳址方式傳遞該元素。 下列範例顯示宣告 replace
中的變更,以便用呼叫程式碼中的另一個陣列取代陣列:
Public Sub replace(ByRef a() As Long)
無法定義多載
如果您想要定義程序的多載版本,您必須使用相同的名稱,但使用不同的簽章。 如果編譯器無法區分宣告與具有相同簽章的多載,則會產生錯誤。
程序的簽章取決於程序名稱和參數清單。 每個多載的名稱必須與所有其他多載相同,但必須與簽章的其中一個其他元件中所有多載的名稱不同。 如需詳細資訊,請參閱 Procedure Overloading。
即使這些項目與參數清單有關,但不是程序簽章的元件:
- 程序修飾詞關鍵字,例如
Public
、Shared
和Static
。 - 參數名稱。
- 參數修飾詞關鍵字,例如
ByRef
和Optional
。 - 傳回值的資料類型 (轉換運算子除外)。
您無法只變更上述一個或多個項目來多載程序。
正確方法:如果要能夠定義程序多載,您必須變更簽章。 因為您必須使用相同的名稱,所以必須變更參數的數目、順序或資料類型。 在泛型程序中,您可以變更型別參數的數目。 在轉換運算子 (CType 函式) 中,您可以變更傳回型別。
使用 Optional 和 ParamArray 引數的多載解析
如果您要多載具有一個或多個 Optional 參數或 ParamArray 參數的程序,則必須避免複製任何隱含多載。 如需詳細資訊,請參閱多載程序中的考量。
呼叫錯誤版本的多載程序
如果程序有多個多載版本,您應該熟悉其所有參數清單,並瞭解 Visual Basic 如何解析多載之間的呼叫。 否則,您可以呼叫預期的多載以外的多載。
您決定要呼叫哪個多載時,務必遵循下列規則:
- 以正確的順序提供正確的引數數目。
- 在理想情況下,您的引數應該具有與對應參數完全相同的資料類型。 在任何情況下,每個引數的資料類型都必須擴大為其對應參數的資料類型。 即使 Opton Strict 陳述式設定為
Off
也是如此。 如果多載需要引數清單的任何縮小轉換,該多載就不適合呼叫。 - 如果您提供需要擴展的引數,請使其資料類型盡可能接近對應的參數資料類型。 如果兩個或多個多載接受引數資料類型,編譯器會解析對呼叫最小擴展量的多載進行的呼叫。
您可以在準備引數時,使用 CType 函式轉換關鍵字來減少不符的資料類型。
多載解析失敗
您呼叫多載程序時,編譯器會嘗試排除其中一個多載之外的所有多載。 如果成功,它會解析該多載的呼叫。 如果它排除所有多載,或無法將適合的多載縮減為單一候選項目,則會產生錯誤。
下列範例說明多載解析程式:
Overloads Sub z(ByVal x As Byte, ByVal y As Double)
End Sub
Overloads Sub z(ByVal x As Short, ByVal y As Single)
End Sub
Overloads Sub z(ByVal x As Integer, ByVal y As Single)
End Sub
Dim r, s As Short
Call z(r, s)
Dim p As Byte, q As Short
' The following statement causes an overload resolution error.
Call z(p, q)
在第一次呼叫中,編譯器會消除第一個多載,因為第一個引數的型別 (Short
) 縮小至對應參數的型別 (Byte
)。 然後它會消除第三個多載,因為第二個多載 (Short
和 Single
) 中的每個引數型別擴展至第三個多載 (Integer
和 Single
) 中的對應型別。 第二個多載需要較少的擴展,因此編譯器會使用它進行呼叫。
在第二次呼叫中,編譯器無法根據縮小而消除任何多載。 它會因為與第一次呼叫中相同的原因而消除第三個多載,因為它可以呼叫第二個多載,而且引數型別的擴展較少。 不過,編譯器無法在第一個和第二個多載之間解析。 每個多載都有一個定義的參數型別,這會擴大為另一個多載中的對應型別 (Byte
擴大為 Short
,但 Single
擴大為 Double
) 。 因此,編譯器會產生多載解析錯誤。
正確方法:若要能夠準確呼叫多載程序,請使用 CType 函式比對引數資料類型與參數型別。 下列範例示範對z
的呼叫,這會強制解析為第二個多載。
Call z(CType(p, Short), CType(q, Single))
使用 Optional 和 ParamArray 引數的多載解析
如果程序的兩個多載具有相同的簽章,不同之處在於最後一個參數在一個中宣告為 Optional,而 ParamArray 則編譯器會根據最接近的相符項目解析該程序的呼叫。 如需詳細資訊,請參閱 Overload Resolution。