程序疑难解答 (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 生成编译器错误,因为它似乎使用错误数据类型的参数(标量 Integer 而非 Integer 数组)调用 AllOnes。 语句 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

以下示例对 increasereplace 进行了调用:

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 调用显示“After increase(n): 11, 21, 31, 41”。 由于 n 是一个引用类型,因此 increase 可以更改其成员,即使它传递了 ByVal

第二个 MsgBox 调用显示“After replace(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

以下项(即使它们与参数列表相关)不是程序签名的组件:

  • 程序修饰符关键字,例如 PublicSharedStatic
  • 参数名称。
  • 参数修饰符关键字,例如 ByRefOptional
  • 返回值的数据类型(转换运算符除外)。

无法通过仅改变上述一项或多项来重载程序。

正确方法:要定义程序重载,必须改变签名。 由于必须使用同一名称,因此必须更改参数的数量、顺序或数据类型。 在泛型程序中,可以更改类型参数的数量。 在转换运算符 (CType Function) 中,可以改变返回类型。

具有 Optional 和 ParamArray 参数的重载决策

如果使用一个或多个 Optional 参数或 ParamArray 参数重载程序,则必须避免重复任何隐式重载。 有关信息,请参阅重载程序的注意事项

调用重载程序的错误版本

如果程序具有多个重载版本,应熟悉其所有参数列表,并了解 Visual Basic 对重载之间的调用的解析方式。 否则,可以调用非预期的重载。

确定要调用的重载后,请注意遵守以下规则:

  • 按正确顺序提供正确的参数数。
  • 理想情况下,参数应具有与相应参数完全相同的数据类型。 在任何情况下,每个参数的数据类型都必须扩大为其对应参数的数据类型。 即使将 Option 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)。 然后,它将消除第三个重载,因为第二个重载中的每个参数类型(ShortSingle)都将扩大到第三个重载中的相应类型(IntegerSingle)。 第二个重载需要更少的扩大,因此编译器将其用于调用。

在第二次调用中,编译器无法根据收缩消除任何重载。 它消除第三个重载的原因与第一次调用相同,因为它可以通过减少扩大参数类型来调用第二个重载。 但编译器无法在第一个和第二个重载之间解析。 每个重载都有一个定义的参数类型,该类型会扩大到其他重载中的相应类型(ByteShortSingleDouble)。 因此,编译器将生成重载决策错误。

正确方法:若要保证准确无误地调用重载程序,请使用 CType 函数将实参数据类型与形参类型匹配。 以下示例显示了对 z 的调用,该调用强制决策到第二个重载。

Call z(CType(p, Short), CType(q, Single))

具有 Optional 和 ParamArray 参数的重载决策

如果程序的两个重载具有相同的签名,则除了最后一个参数在一个重载中声明为 Optional,在另一个重载中声明为 ParamArray 外,编译器会根据最接近的匹配来解析对该程序的调用。 有关详细信息,请参阅 Overload Resolution

另请参阅