VBA-Урок 9. Процедуры и функции
На данный момент, все процедуры, мы создавали, имеют тип Public , что означает, что они доступны из любого модуля.
Sub example() 'Идентична к: Public Sub example()
Чтобы сделать процедуру доступной только в определенном модуле, используется ключевое слово Private:
Private Sub example()
Запуск процедуры с середины другой процедуры
Чтобы выполнить процедуру с середины другой процедуры, просто введите ее название.
Здесь есть очень простой пример:
Private Sub warning() MsgBox "Caution . " End Sub Sub macro_test() If Range("A1") = "" Then warning 'Аргументы
Аргументы делают возможным использование значений из процедуры в под-процедуры (запомните, что по умолчанию, переменные являются доступны только по той процедуры, в которой они были объявлены).
Private Sub warning(var_text As String) MsgBox "Caution : " & var_text & " !" End Sub Sub macro_test() If Range("A1") = "" Then warning "пустая ячейка" ElseIf Not IsNumeric(Range("A1")) Then warning "нецифровое значение" End If End Sub
К процедуре "warning" был добавлен аргумент, в данном случае это переменная "var_text" с типом "String" (строка):
Private Sub warning(var_text As String)Эта процедура требует аргумент, поэтому мы должны поставить значение после "warning", чтобы выполнить ее:
warning "пустая ячейка"Когда мы хотим прописать несколько аргументов, тогда они должны быть отделены запятыми.
Необязательные аргументы
По умолчанию, если процедура имеет аргументы, то они должны быть обязательно проставлены, и если они не проставлены, тогда процедура не выполнится.
Необязательный аргумент может быть добавлен после обязательного, с помощью ключевого слова Optional . Например:
Private Sub dialog_boxes(last_name As String, Optional first_name, Optional age)Теперь эта процедура может быть выполнена с или без опционального аргумента, как здесь:
'Пример 1: отображаем фамилию: dialog_boxes last_name1 'Пример 2: отображаем фамилию и имя: dialog_boxes last_name1, first_name1 'Пример 3: отображаем фамилию и возраст: dialog_boxes last_name1, , age1 'Пример 4: отображаем фамилию, имя и возраст: dialog_boxes last_name1, first_name1, age1Аргументы должны быть введены в правильном порядке.
Чтобы протестировать, присутствует ли опциональный аргумент в процедуре, мы используем функцию IsMissing . Эта функция совместима только с некоторыми типами функций (типа Variant) и это является решающим, так как тип необязательно аргументов не был указан в объявлении (необъявленный тип = Variant).
Здесь есть пример, который использует два фрагмента кода, которые рассматривались выше:
Sub macro_test() Dim last_name1 As String, first_name1 As String, age1 As Integer last_name1 = Range("A1") first_name1 = Range("B1") age1 = Range("C1") 'Пример 1: отображаем фамилию: dialog_boxes last_name1 'Пример 2: отображаем фамилию и имя: dialog_boxes last_name1, first_name1 'Пример 3: отображаем фамилию и возраст: dialog_boxes last_name1, , age1 'Пример 4: отображаем фамилию, имя и возраст: dialog_boxes last_name1, first_name1, age1 End Sub Private Sub dialog_boxes(last_name As String, Optional first_name, Optional age) If IsMissing(age) Then 'Если переменная age отсутствует. If IsMissing(first_name) Then 'Если переменная first_name отсутствует, тогда 'будет отображаться только фамилия MsgBox last_name Else 'В противном случае, будет отображаться фамилия и имя MsgBox last_name & " " & first_name End If Else 'Если переменная age присутствует. If IsMissing(first_name) Then 'Если переменная first_name отсутствует, тогда 'будет отображено фамилию и возраст MsgBox last_name & ", " & age & " лет" Else 'В противном случае будет отображено фамилию, имя и возраст MsgBox last_name & " " & first_name & ", " & age & " лет" End If End If End SubСм. рисунок ниже (пример 1):
ByRef - ByVal
По умолчанию, аргументы имеют тип ByRef , что означает: если переменная передается как аргумент, ссылка на нее будет также передаваться. Иными словами, если переменная была изменена другой под-процедурой, то она также будет изменена во внешней процедуре, которая вызывает эту под-процедуру.
Sub macro_test() Dim var_number As Integer var_number = 30 calcul_square var_number MsgBox var_number End Sub Private Sub calcul_square(ByRef var_value As Integer) 'ByRef не обязательно указывать '(является значением по умолчанию) var_value = var_value * var_value End SubЧтобы стало понятнее, ниже есть пример того, что произойдет, если макрос будет запущен на выполнение:
var_number = 30 'Начальное значение переменной "var_number" є 30 calcul_square var_number 'Под-процедура запускается с "var_number" как аргумент Private Sub calcul_square(ByRef var_value As Integer) 'Переменная "var_value" в некоторой степени служит для быстрого доступа к переменной "var_number", 'что означает, что если переменная "var_value" изменена, переменная "var_number" будет также изменена "(и они не должны обязательно иметь одинаковое имя) var_value = var_value * var_value 'Значение переменной "var_value" изменено (и поэтому "var_number" также одновременно изменено) End Sub 'Конец под-процедуры MsgBox var_number 'Переменная "var_number" была изменена, поэтому 900 будет сейчас отображено в диалоговом окнеВторой метод заключается в использовании ByVal .
В отличие от ByRef , который передает ссылки (ярлык), ByVal передает значение, которое означает, что значение передано как аргумент не было изменено.
Ниже вы можете увидеть как предыдущий код и ByVal работают:
var_number = 30 'Начальное значение переменной "var_number" есть 30 calcul_square var_number 'Под-процедура запускается с "var_number" как аргумент Private Sub calcul_square (ByVal var_value As Integer) 'Переменная "var_value" копирует значение переменной "var_number" (2-е переменные не являются связанными) var_value = var_value * var_value 'Значение переменной "var_value" изменено End Sub 'Конец под-процедуры (в этом примере под-процедура не имеет никакого влияния ни на что) MsgBox var_number 'Переменная "var_number" не была изменена, и поэтому 30 будет отображено в диалоговом окнеЧто вам нужно запомнить: используйте ByVal когда переменная не должна быть изменена .
Функции
Основным отличием между процедурой и функцией является то, что функция возвращает значение.
Вот простой пример:
Function square(var_number) square = var_number ^ 2 'Функция "square" возвращает значение "корень квадратный" End Function Sub macro_test() Dim result As Double result = square(9.876) 'Переменной result присваивается значение, которое было рассчитано функцией MsgBox result 'Отображается результат (в данном случае квадрат для 9.876) End SubФункция может быть использована на рабочем листе, подобно любой другой функции в Excel.
Например, чтобы получить квадрат значения, которое введенное в ячейку A1:
Byval vba что это
Давайте посмотрим как определить событие выбора определной ячейки. Первое - это надо определить, что вообще что-то выбрали. Для этого создайте книгу. Запустите редактор VBA и щелкните на листе. Для данного листа будет создана функция SelectionChange, если ее нет, то у Вас всегда есть возможность ее выбрать. Теперь впишите код сообщения в функцию, чтобы увидеть, что происходит.
Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range) MsgBox Target.Address End SubУ Вас теперь есть возможность щелкать по ячейкам конкретного листа, я еще раз повторяю и вы получите сообщение с адресом ячейки. Сообщение выбора ячейки обрабатывается локально. А что если у нас надо обрабатывать это сообшение с трех листов и желательно вместе. Для этого нужно создать модуль и в нем процедуру обработки. Я пока поместил туда просто вывод информационного окна.
Public Sub Selection_Cell(ByVal Addres As String, ByVal List As String) MsgBox Addres + " " + List End SubИ соотвественно надо пересылать туда данный с каждого листа.
Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range) Selection_Cell Target.Address, Target.Worksheet.Name End SubВот с этого момента можно вставлять вызов функции Selection_Cell на каждый лист в ответ на реакцию выделения.
Но это еще не все. Дело в том, что передается объект типа RANGE, то есть нет разницы передана одна ячейка или диапазон. Но при выделении диапазона внутрь него может попасть и наша ячейка. Итак, нам надо бы анализировать диапазон. Давайте договоримся, что нам надо обязательно отловить момент выделения ячейки $A$1. Вот на всех листах. У нас для этого все есть. Процедура общая, в которой есть адрес. И реакция на выделение на каждом листе. Смотрим код:
Public Sub Selection_Cell(ByVal Addres As String, ByVal List As String) On Error GoTo Ends Dim Test As Range Dim Find As Range Dim Result As Range Dim x As Integer Set Find = Range("$A$1") Set Test = Range(Addres) Set Result = Intersect(Test, Find) x = Result.Count MsgBox "$A$1" Ends: End SubИдея простая. Мы имеем переданный диапазон Test и нужный Find. Как узнать, что один включает другой . Вызовем операцию пересечения Intersect. Результатом будет диапазон, который содержит пересечение. Так вот если искомая нам ячейка в нем есть, то нормально, а если нет, то обращение к Count вызовет ошибку. Этим мы и воспользовались.
Byval vba что это
Описывает имя, аргументы и текст программы, составляющий тело процедуры Function.
[Public | Private] [Static] Function имя [(списокАргументов)] [As тип] [инструкции] [имя = выражение] [Exit Function] [инструкции] [имя = выражение] End Function
- Optional - Необязательный. Указывает, что этот аргумент необязателен. При использовании этого элемента все последующие аргументы, которые содержит списокАргументов, также должны быть необязательными и быть описаны с помощью ключевого слова Optional. Не допускается использование ключевого слова Optional для любого из аргументов, если используется ключевое слово ParamArray.
- ByVal - Необязательный. Указывает, что этот аргумент передается по значению.
- ByRef - Необязательный. Указывает, что этот аргумент передается по ссылке. Описание ByRef используется в Visual Basic по умолчанию.
- ParamArray - Необязательный. Используется только в качестве последнего элемента в списке списокАргументов для указания, что конечным аргументом является описанный как Optional массив типа Variant. Ключевое слово ParamArray позволяет задавать произвольное количество аргументов. Оно не может быть использовано со словами ByVal, ByRef или Optional.
- имяПеременной - Обязательный. Имя переменной, удовлетворяющее стандартным правилам именования переменных.
- тип - Необязательный. Тип данных аргумента, передаваемого в процедуру; допускаются типы Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (в настоящее время не поддерживается) Date, String (только строки переменной длины), Object, Variant. Если отсутствует ключевое слово Optional, допускается указание определяемого пользователем типа или объектного типа.
- поУмолчанию - Необязательный. Любая константа или выражение, дающее константу. Используется только вместе с параметром Optional. Если указан тип Object, единственным значением по умолчанию может быть значение Nothing.
тип
Необязательный. Тип данных значения, возвращаемого процедурой Function, поддерживаются типы Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (в настоящее время не поддерживается), Date, String (за исключением строк фиксированной длины), Object, Variant или любой определяемый пользователем тип. Невозможен возврат массивов любого типа, однако допускается возврат значения типа Variant, содержащего массив.
инструкции
Необязательный. Любая группа инструкций, выполняемых внутри процедуры Function.
выражение
Необязательный. Возвращаемое значение процедуры Function.
Замечания
Процедуры Function, не описанные явно с помощью ключевых слов Public или Private, по умолчанию являются общими. Если не используется ключевое слово Static, значения локальных переменных не сохраняются между вызовами.
Допускаются рекурсивные процедуры Function; то есть они могут вызывать сами себя для выполнения определенных задач. Однако рекурсия может приводить к переполнению стека. Ключевое слово Static обычно не используется с рекурсивными процедурами Function.
Все выполняемые команды должны содержаться в процедурах. Не допускается определение процедуры Function внутри другой процедуры Function, Sub или Property.
Инструкция Exit Function приводит к немедленному выходу из процедуры Function. Выполнение программы продолжается с инструкции, следующей за инструкцией, в которой была вызвана процедура Function. В любом месте процедуры Function возможно наличие любого числа инструкций Exit Function.
Подобно процедуре Sub, процедура Function является самостоятельной процедурой, которая может получать аргументы, выполнять последовательность инструкций и изменять значения своих аргументов. Однако в отличие от процедуры Sub, когда требуется использовать возвращаемое функцией значение, процедура Function может применяться в правой части выражения, как и любая другая встроенная функция, например, Sqr, Cos или Chr.
Процедура Function вызывается в выражении по своему имени, за которым следует список аргументов в скобках. Для получения дополнительных сведений о вызовах процедур Function см. описание инструкции Call.
Для возврата значения из функции следует присвоить значение имени функции. Любое число таких инструкций присвоения может находиться в любом месте процедуры. Если имени процедуры не присваивается никакого значения, процедура возвращает значение по умолчанию: числовая функция возвращает нулевое значение, строковая функция возвращает значение пустой строки (""), а функция типа Variant возвращает значение Empty. Функция, которая возвращает ссылку на объект, возвращает значение Nothing, если внутри процедуры Function никакая ссылка на объект не присваивается имени функции с помощью инструкции Set.
В следующем примере демонстрируется присвоение возвращаемого значения функции BinarySearch. В этом случае имени присваивается значение False, чтобы указать, что некоторое значение не было найдено.
Function BinarySearch(. . .) As Boolean . . . ' Значение не найдено. Возвращается значение False. If lower > upper Then BinarySearch = False Exit Function End If . . . End Function
Переменные, используемые в процедурах Function, разбиваются на две категории: явно описанные внутри процедуры и не описанные внутри процедуры. Переменные, которые явно описаны в процедуре (с помощью ключевого слова Dim или эквивалентного ему), всегда являются локальными для этой процедуры. Переменные, которые используются, но явно не описаны в процедуре, также являются локальными, если они явно не описаны на более высоком уровне.
В процедуре допускается использование переменной, которая явно не описана в этой процедуре, но при этом может возникать конфликт имен, если на уровне модуля был определен элемент с тем же самым именем. Если в процедуре содержится ссылка на неописанную переменную, имя которой совпадает с именем другой процедуры, константы или переменной, то подразумевается, что процедура ссылается на это имя на уровне модуля. Чтобы избежать возникновения конфликтов такого рода, следует пользоваться явным описанием переменных. Для включения обязательного явного описания переменных используется инструкция Option Explicit.
В языке Visual Basic возможна реорганизация арифметических выражений для увеличения скорости их выполнения. Избегайте применения процедуры Function в арифметическом выражении, если эта процедура изменяет значения переменных, используемых в этом же выражении.
Пример
В данном примере инструкция Function используется для объявления имени, аргументов и кода, составляющих основу процедуры Function. В последнем примере используются инициализированные аргументы Optional с заданным типом.
' Следующая функция, определенная пользователем, возвращает ' квадратный корень из переданного ей аргумента. Function CalculateSquareRoot(NumberArg As Double) As Double If NumberArg < 0 Then ' Анализирует аргумент. Exit Function ' Возврат в вызывающую ' процедуру. Else CalculateSquareRoot = Sqr(NumberArg) ' Возвращает квадратный ' корень. End If End Function
Ключевое слово ParamArray позволяет функции принимать переменное число аргументов. В следующем описании FirstArg передается по значению.
Function CalcSum(ByVal FirstArg As Integer, ParamArray OtherArgs()) Dim ReturnValue ' Если функция вызывается следующим образом: ReturnValue = CalcSum(4, 3 ,2 ,1) ' Локальные переменные принимают следующие значения: FirstArg = 4, ' OtherArgs(1) = 3, OtherArgs(2) = 2 и т.п., если нижняя граница ' индексов массивов равняется 1.
Аргументы Optional Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 14.02.2002
Форум пользователей Visio
При размещении файлов предпочтительным является формат vsd (а не vsdx/vsdm)
Размещая ваши вложения на форуме не используйте имена файлов содержащих кириллицу, в противном случае файл будет иметь имя . !
Для форматирования ваших сообщений используйте BBCodes, описание используемых на форуме BBCodes.
VBA ByVal, ByRef, ссылки и как со всем этим работать
Заголовок сообщения: VBA ByVal, ByRef, ссылки и как со всем этим работать
Добавлено: 26 май 2014, 11:12
Не могу найти в интернете исчерпывающей информации по использованию ByVal, ByRef и всего, что с этим связано.
Пример кода:
Private Sub Document_DocumentOpened(ByVal Doc As IVDocument)
' Не соответствие типов
MyApplication_ViewChanged (Application.ActiveWindow)
' Так тоже несоответствие типов
Dim Window As Visio.IVWindow
Set Window = Application.ActiveWindow
MyApplication_ViewChanged (Window)
End Sub
Private Sub MyApplication_ViewChanged(ByVal Window As IVWindow)
End Sub
Вопрос:
Как корректно самостоятельно вызвать обработчик события. И что вообще из себя представляют IV Document и прочие объекты. Как я понимаю это интерфейсы, но как они получаются, как связаны с объектами? Или ищу не там или ищу не то . прошу помощи у знающих людей. Даже просто за правильное направление буду благодарен.
Заголовок сообщения: Re: VBA ByVal, ByRef, ссылки и как со всем этим работать
Добавлено: 26 май 2014, 11:25
Tanatos писал(а):
Не могу найти в интернете исчерпывающей информации по использованию ByVal, ByRef и всего, что с этим связано.
IVDocument - это как я понял, тип данных для документов в формате визио. Признаться я сам с событий не создавал
посмотри здесь, главу 20.