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


Устранение неполадок с процедурами (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 является ссылочным типом, increase его члены могут изменяться, даже если он передается ByVal.

Второй MsgBox вызов отображает "После замены(n): 11, 21, 31, 41". Так как n передается ByVal, replace переменная n не может измениться, назначив ему новый массив. При replace создании нового экземпляра k массива и назначении его локальной переменной aона теряет ссылку, n передаваемую вызывающим кодом. Когда он увеличивает члены a, затрагивается только локальный массив k .

Правильный подход: Чтобы изменить сам базовый элемент переменной, передайте его по ссылке. В следующем примере показано изменение в объявлении replace , которое позволяет заменить один массив другим в вызывающем коде:

Public Sub replace(ByRef a() As Long)

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

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

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

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

  • Ключевые слова модификатора процедуры, такие как Public, Sharedи Static.
  • Имена параметров.
  • Ключевые слова модификатора параметров, такие как ByRef и Optional.
  • Тип данных возвращаемого значения (за исключением оператора преобразования).

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

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

Разрешение перегрузки с аргументами Optional и ParamArray

При перегрузке процедуры с одним или несколькими необязательными параметрами или параметром ParamArray необходимо избежать дублирования любых неявных перегрузок. Дополнительные сведения см. в разделе "Рекомендации в процедурах перегрузки".

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

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

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

  • Укажите правильное количество аргументов и в правильном порядке.
  • В идеале аргументы должны иметь те же типы данных, что и соответствующие параметры. В любом случае тип данных каждого аргумента должен расшириться до соответствующего параметра. Это верно, даже если для параметра задано 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

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

См. также