Поделиться через


Устранение неполадок в процедурах (Visual Basic)

На этой странице перечислены некоторые общие проблемы, которые могут возникнуть при работе с процедурами.

Возврат типа массива из процедуры Function

Если процедура Function возвращает тип данных массива, имя Function нельзя использовать для хранения значений элементов массива. Попытку выполнить такое действие компилятор интерпретирует как вызов Function. В приведенном ниже примере создаются ошибки компилятора.

Function allOnes(ByVal n As Integer) As Integer()

For i As Integer = 1 To n - 1

' The following statement generates a COMPILER ERROR.

allOnes(i) = 1

Next i

' The following statement generates a COMPILER ERROR.

Return allOnes()

End Function

Оператор allOnes(i) = 1 создает ошибку компилятора, поскольку вызывает allOnes с аргументом неверного типа данных (одноэлементный Integer вместо массива Integer). Оператор Return allOnes() создает ошибку компилятора, поскольку вызывает allOnes без аргументов.

Правильный подход Чтобы иметь возможность изменять элементы массива для его возвращения, определите внутренний массив в качестве локальной переменной. Следующий пример компилируется без ошибок.

Function allOnes(ByVal n As Integer) As Integer()
    Dim i As Integer, iArray(n) As Integer 
    For i = 0 To n - 1
        iArray(i) = 1
    Next i
    Return iArray
End Function

Аргумент не изменен вызовом процедуры

Если предполагается позволить процедуре изменять программный элемент базового аргумента в вызывающем коде, необходимо передать его по ссылке. Но процедура может получить доступ к элементам аргумента ссылочного типа даже в том случае, если передать его по значению.

  • Базовая переменная. Чтобы разрешить процедуре заменить значение элемента базовой переменной, процедура должна объявить параметр ByRef (Visual Basic). Также, вызывающий код не должен заключать аргумент в круглые скобки, поскольку это переопределяет механизм передачи ByRef.

  • Элементы ссылочного типа. Если объявляется параметр ByVal (Visual Basic), процедура не может изменить элемент базовой переменной. Однако если аргумент имеет ссылочный тип, процедура может изменять члены указываемого ей объекта, даже если она не может заменить значение переменной. Например, если аргумент является переменной массива, ему нельзя присвоить новый массив, но можно изменить один или несколько его элементов. Изменение элементов будет отражено в базовой переменной массива в вызывающем коде.

В следующем примере определяются две процедуры, которые принимают переменную массива по значению и производят действия над его элементами. Процедура 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 выводит следующее: "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)

Не удалось определить перегрузку

Если требуется определить перегруженную версию процедуры, необходимо использовать то же имя, но другую сигнатуру. Если компилятор не может отличить объявление от перегруженного варианта с той же сигнатурой, он выдает ошибку.

Сигнатура процедуры определяется именем процедуры и списком параметров. Каждая перегрузка должна иметь то же имя, что все другие перегрузки, но должна отличаться от всех, по крайней мере, по одному из компонентов сигнатуры. Дополнительные сведения см. в разделе Перегрузка процедур (Visual Basic).

Следующие элементы, даже если они относятся к списку параметров, не являются компонентами сигнатуры процедуры.

  • Ключевые слова модификаторов процедур, например Public, Shared и Static

  • Имена параметров

  • Ключевые слова модификаторов процедур, например ByRef и Optional

  • Тип данных возвращаемого значения (за исключением оператора преобразования)

Нельзя перегрузить процедуру, отличающуюся по одному или нескольким перечисленных выше элементов.

Правильный подход Чтобы определить перегрузку процедуры, необходимо иметь различающиеся сигнатуры. Так как необходимо использовать то же имя, должны меняться число, порядок следования или типы данных параметров. В универсальной процедуре можно варьировать число параметров-типов. В операторе преобразовании (Функция CType (Visual Basic)) можно варьировать возвращаемый тип.

Разрешение перегрузки с помощью аргументов Optional и ParamArray

Если происходит перегрузка процедуры с одним или несколькими параметрами Optional (Visual Basic) или параметром ParamArray (Visual Basic), следует избегать дублирования неявных перегрузок. Дополнительные сведения см. в разделе Вопросы, связанные с перегрузкой процедур (Visual Basic).

Вызов неверной версии перегруженной процедуры

Если процедура имеет несколько перегруженных версий, следует ознакомиться со всеми их списками параметров и понять, как Visual Basic разрешает вызовы между перегрузками. В противном случае можно вызвать перегрузку, отличную от предполагаемой.

После определения перегрузки, которую следует вызвать, будьте внимательны и соблюдайте следующие правила.

  • Укажите правильное количество аргументов в требуемом порядке.

  • В идеальном случае аргументы должны иметь те же типы данных, что и соответствующие параметры. В любом случае, тип данных каждого аргумента должен расширяться до типа соответствующего параметра. Это верно, даже если Оператор Option Strict имеет значение Off. Если перегрузка требует сужающего преобразования списка аргументов, она не может быть вызвана.

  • При указании аргументов, которые требуют расширения, задайте их типы данных максимально близкими к типам данных параметров. Если две или более перегрузки принимают типы данных аргументов, компилятор разрешает вызов перегрузки, вызывая вариант с наименьшим расширением.

Можно уменьшить вероятность несоответствия типов данных с помощью ключевого слова преобразования Функция CType (Visual Basic) при подготовке аргументов.

Сбой разрешения перегрузки

При вызове перегруженной процедуры компилятор пытается выбрать единственную перегрузку. Если это происходит успешно, он разрешает вызов этой перегрузки. Если исключаются все перегрузки или не может быть выбран единственный вариант, генерируется ошибка.

Процесс разрешения перегрузки показан в следующем примере.

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 (Visual Basic) для соответствия типов данных аргументов типам параметров. В следующем примере показан вызов z, требующий разрешения второй перегрузки.

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

Разрешение перегрузки с помощью аргументов Optional и ParamArray

Если две перегрузки процедуры имеют идентичные сигнатуры за исключением того, что последний параметр в одной из них объявляется как Optional (Visual Basic) и как ParamArray (Visual Basic) в другой, компилятор разрешает вызов процедуры, выбирая наиболее близкий вариант. Дополнительные сведения см. в разделе Разрешение перегрузки (Visual Basic).

См. также

Основные понятия

Процедуры в Visual Basic

Подпрограммы (Visual Basic)

Процедуры Function (Visual Basic)

Процедуры свойств (Visual Basic)

Процедуры операторов (Visual Basic)

Параметры и аргументы процедуры (Visual Basic)

Перегрузка процедур (Visual Basic)

Вопросы, связанные с перегрузкой процедур (Visual Basic)

Разрешение перегрузки (Visual Basic)