Цикл with в миф

  • 07/20/2015
  • Чтение занимает 2 мин

В этой статье

Выполняет последовательность операторов, которые многократно ссылаются на единственный объект или структуру, чтобы операторы могли использовать упрощенный синтаксис доступ к членам объекта или структуры. При использовании структуры можно только считывать значения членов или вызвать методы. При попытке присвоения значений членам структуры, используемым в операторе With…End With, возникает ошибка.

Синтаксис

With objectExpression [ ements ] End With

Компоненты

ТерминОпределение
objectExpressionОбязательный. Выражение, результатом которого является объект. Выражение может быть произвольно сложным и вычисляется только один раз. Результатом выражения могут быть данные любого типа, включая простейшие типы.
ementsНеобязательный элемент. Один или несколько операторов между With и End With, которые могут ссылаться на члены объекта, создаваемого при вычислении выражения objectExpression.
End WithОбязательный элемент. Завершает определение блока With.

С помощью With…End With можно выполнять последовательность операторов с указанным объектом без необходимости многократного указания имени объекта. В блоке операторов With члены объекта можно указывать начиная с точки, как если бы перед ней стоял объект оператора With.

Например, чтобы изменить несколько свойств одного объекта, поместите операторы присваивания свойств внутрь блока With…End With и укажите объект лишь один раз, а не по одному разу для каждого свойства.

Если код обращается к одному и тому же объекту в нескольких операторах, оператор With обеспечивает следующие преимущества:

  • Не требуется многократно вычислять сложное выражение или присваивать результат временной переменной, чтобы несколько раз сослаться на членов объекта.

  • За счет исключения повторяющихся определяющих выражений код становится более понятным.

Тип данных у objectExpression может быть любым типом класса или структуры или даже простейшим типом Visual Basic (например Integer). При результат вычисления выражения objectExpression не является объектом, можно только считывать значения его членов или вызвать методы. При попытке присвоения значений членам структуры, используемым в операторе With…End With, возникает ошибка. Это та же самая ошибка, которая возникает, если сразу после вызова метода, возвращающего структуру, попытаться обратиться к члену результата функции и присвоить ему значение, например GetAPoint().x = 1. Проблема в обоих случаях заключается в том, что структура существует только в стеке вызовов – в таких ситуациях не существует способа записи измененного члена структуры в некоторое расположение таким образом, чтобы весь остальной код программы мог видеть это изменение.

Выражение objectExpression вычисляется один раз при входе в блок. Переназначение выражения objectExpression изнутри блока With невозможно.

Внутри блока With к методам и свойствам только указанного объекта можно обращаться без их полного описания. Можно использовать методы и свойства других объектов, однако необходимо полностью указывать их имена.

Оператор With…End With можно разместить внутри другого такого оператора. Вложенные операторы With…End With могут быть сложны для понимания, если указываемые объекты не очевидны из контекста. Необходимо указывать полную ссылку на объект, находящийся во внешнем блоке With, при ссылке на него из внутреннего блока With.

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

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

Примечание

Ключевое слово With можно также использовать в инициализаторах объектов. Дополнительные сведения и примеры см. в разделе инициализаторы объектов: именованные и анонимные типы и анонимные типы.

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

Пример

В следующем примере в каждом блоке With выполняется несколько операторов для одного объекта.

Private Sub AddCustomer() Dim theCustomer As New Customer With theCustomer .Name = “Coho Vineyard” .URL = “https://www.cohovineyard.com/” .City = “Redmond” End With With theCustomer.s .Add(“First .”) .Add(“Second .”) End With End Sub Public Class Customer Public Property Name As String Public Property City As String Public Property URL As String Public Property s As New List(Of String) End Class

Пример

В следующем примере показаны вложенные операторы With…End With: Во вложенном операторе With этот синтаксис относится к внутреннему объекту.

Dim theWindow As New EntryWindow With theWindow With .Label .Content = “This is a message.” .Foreground = Brushes.DarkSeaGreen .Background = Brushes.LightYellow End With .Title = “The Form Title” .Show() End With

См. также

  • List<T>
  • Вложенные структуры управления
  • Инициализаторы объектов: именованные и анонимные типы
  • Анонимные типы

Источник

Встречаются ситуации, когда от программы VBA требуется совершить несколько раз подряд один и тот же набор действий (то есть повторить несколько раз один и тот же блок кода). Это может быть сделано при помощи циклов VBA.

К циклам VBA относятся:

  • Цикл For
  • Цикл Do While
  • Цикл Do Until

Далее мы подробно рассмотрим каждый из этих циклов.

Оператор цикла «For» в Visual Basic

Структура оператора цикла For в Visual Basic может быть организована в одной из двух форм: как цикл For … Next или как цикл For Each.

Цикл «For … Next»

Цикл For … Next использует переменную, которая последовательно принимает значения из заданного диапазона. С каждой сменой значения переменной выполняются действия, заключённые в теле цикла. Это легко понять из простого примера:

For i = 1 To 10 Total = Total + iArray(i) Next i

В этом простом цикле For … Next используется переменная i, которая последовательно принимает значения 1, 2, 3, … 10, и для каждого из этих значений выполняется код VBA, находящийся внутри цикла. Таким образом, данный цикл суммирует элементы массива iArray в переменной Total.

Читайте также:  Анализ цикла куликово поле

В приведённом выше примере шаг приращения цикла не указан, поэтому для пошагового увеличения переменной i от 1 до 10 по умолчанию используется приращение 1. Однако, в некоторых случаях требуется использовать другие значения приращения для цикла. Это можно сделать при помощи ключевого слова Step, как показано в следующем простом примере.

For d = 0 To 10 Step 0.1 dTotal = dTotal + d Next d

Так как в приведённом выше примере задан шаг приращения равный 0.1, то переменная dTotal для каждого повторения цикла принимает значения 0.0, 0.1, 0.2, 0.3, … 9.9, 10.0.

Для определения шага цикла в VBA можно использовать отрицательную величину, например, вот так:

For i = 10 To 1 Step -1 iArray(i) = i Next i

Здесь шаг приращения равен -1, поэтому переменная i с каждым повторением цикла принимает значения 10, 9, 8, … 1.

Цикл «For Each»

Цикл For Each похож на цикл For … Next, но вместо того, чтобы перебирать последовательность значений для переменной-счётчика, цикл For Each выполняет набор действий для каждого объекта из указанной группы объектов. В следующем примере при помощи цикла For Each выполняется перечисление всех листов в текущей рабочей книге Excel:

Dim wSheet As Worksheet For Each wSheet in Worksheets MsgBox “Найден лист: ” & wSheet.Name Next wSheet

Оператор прерывания цикла «Exit For»

Оператор Exit For применяется для прерывания цикла. Как только в коде встречается этот оператор, программа завершает выполнение цикла и переходит к выполнению операторов, находящихся в коде сразу после данного цикла. Это можно использовать, например, для поиска определённого значения в массиве. Для этого при помощи цикла просматривается каждый элемент массива. Как только искомый элемент найден, просматривать остальные нет необходимости – цикл прерывается.

Применение оператора Exit For продемонстрировано в следующем примере. Здесь цикл перебирает 100 записей массива и сравнивает каждую со значением переменной dVal. Если совпадение найдено, то цикл прерывается:

For i = 1 To 100 If dValues(i) = dVal Then IndexVal = i Exit For End If Next i

Цикл «Do While» в Visual Basic

Цикл Do While выполняет блок кода до тех пор, пока выполняется заданное условие. Далее приведён пример процедуры Sub, в которой при помощи цикла Do While выводятся последовательно числа Фибоначчи не превышающие 1000:

‘Процедура Sub выводит числа Фибоначчи, не превышающие 1000 Sub Fibonacci() Dim i As Integer ‘счётчик для обозначения позиции элемента в последовательности Dim iFib As Integer ‘хранит текущее значение последовательности Dim iFib_Next As Integer ‘хранит следующее значение последовательности Dim iStep As Integer ‘хранит размер следующего приращения ‘инициализируем переменные i и iFib_Next i = 1 iFib_Next = 0 ‘цикл Do While будет выполняться до тех пор, пока значение ‘текущего числа Фибоначчи не превысит 1000 Do While iFib_Next < 1000 If i = 1 Then ‘особый случай для первого элемента последовательности iStep = 1 iFib = 0 Else ‘сохраняем размер следующего приращения перед тем, как перезаписать ‘текущее значение последовательности iStep = iFib iFib = iFib_Next End If ‘выводим текущее число Фибоначчи в столбце A активного рабочего листа ‘в строке с индексом i Cells(i, 1).Value = iFib ‘вычисляем следующее число Фибоначчи и увеличиваем индекс позиции элемента на 1 iFib_Next = iFib + iStep i = i + 1 Loop End Sub

В приведённом примере условие iFib_Next < 1000 проверяется в начале цикла. Поэтому если бы первое значение iFib_Next было бы больше 1000, то цикл бы не выполнялся ни разу.

Другой способ реализовать цикл Do While – поместить условие не в начале, а в конце цикла. В этом случае цикл будет выполнен хотя бы раз, не зависимо от того, выполняется ли условие.

Схематично такой цикл Do While с проверяемым условием в конце будет выглядеть вот так:

Do … Loop While iFib_Next < 1000

Цикл «Do Until» в Visual Basic

Цикл Do Until очень похож на цикл Do While: блок кода в теле цикла выполняется раз за разом до тех пор, пока заданное условие выполняется (результат условного выражения равен True). В следующей процедуре Sub при помощи цикла Do Until извлекаются значения из всех ячеек столбца A рабочего листа до тех пор, пока в столбце не встретится пустая ячейка:

iRow = 1 Do Until IsEmpty(Cells(iRow, 1)) ‘Значение текущей ячейки сохраняется в массиве dCellValues dCellValues(iRow) = Cells(iRow, 1).Value iRow = iRow + 1 Loop

В приведённом выше примере условие IsEmpty(Cells(iRow, 1)) находится в начале конструкции Do Until, следовательно цикл будет выполнен хотя бы один раз, если первая взятая ячейка не пуста.

Однако, как было показано в примерах цикла Do While, в некоторых ситуациях нужно, чтобы цикл был выполнен хотя бы один раз, не зависимо от первоначального результата условного выражения. В таком случае условное выражение нужно поместить в конце цикла, вот так:

Do … Loop Until IsEmpty(Cells(iRow, 1))

Оцените качество статьи. Нам важно ваше мнение:

Источник

Доброго времени суток! Данную статью я решил посвятить рубрике по основам программирования в Visual Basic for Application. И сегодня мы поговорим о циклах в VBA, разберём их синтаксис и рассмотрим несколько примеров, которые часто встречаются программисту.

Сначала напомню, что цикл – процесс повторения части кода, выполняемый, пока не будет выполнено или нарушено заданное нами условие.

В данной статье мы разберём синтаксис и примеры следующих циклов в VBA:

  • For
  • For each
  • While
  • Until

Цикл For в VBA

Блок схема цикла for

Цикл for в VBA обычно используется при зацикливании фрагмента кода, если нам известно конечное значение counter – счетчика, при котором мы выйдем из цикла.

Читайте также:  Цикл с переменной в питоне

Возьмём для примера самый распространённый пример:

Сгенерировать массив из 5 целых значений

Dim mas(5) As Integer For i% = 0 To 4 mas(i) = Int((10 * Rnd) + 1) Next i

Обратите ваше внимание, что в этом примере используется неявное объявление при работе с циклами в VBA. i% – означает неявное объявление переменной i в формате integer. Такая конструкция по сути заменяет следующую: dim i as integer. Это используется для сокращения кода и для удобства написания и чтения. В старых версиях VBA необходимо указывать знак формата после каждого использования неявной переменной. В более поздних версиях достаточно всего один раз.

VBA для цикла for даёт возможность использовать функцию Step. Как ясно из перевода, это шаг, с которым мы будем проходить наш интервал. По умолчанию, он равен 1. Популярный вариант использования встречается в случаях, когда counter связан с переменной, используемой внутри цикла. Например, при написании программ, связанных с функциями.

Найти пересечение графика функции y = 5*x + 5 с осью ординат

expr(x As Integer) As Integer expr = 5 * x + 5 End Sub CodeTown() Dim i As Integer For i = -10 To 10 Step 1 If expr(i) = 0 Then MsgBox “При Y = 0, Х = “+ CStr(i) Next i End Sub

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

expr(x As Integer) As Integer expr = 5 * x + 5 End Sub CodeTown() Dim i As Integer For i = -10 To 10 Step 1 If expr(i) = 0 Then MsgBox “При Y = 0, Х = “+ CStr(i) Exit For End If Next i End Sub

C помощью команды Exit можно закончить выполнение любого цикла в VBA. Достаточно указать после Exit название используемого цикла. Также им возможно завершить работу любой процедуры или функции.

Цикл For Each в VBA

Блок схема For Each

For Each в VBA основан на переборе всех элементов, указанного типа в массиве, объекте или группе.

Самый популярный вариант его использования – перебор страниц в рабочей книге.

Вывести названия всех листов в рабочей книге

For Each ws In ThisWorkbook.Worksheets MsgBox ws.Name Next ws

И ещё один интересный пример:

Изменить размер шрифта и выравнить по центру текст в label

For Each element In UserForm1.Controls If InStr(1, UserForm1.Controls.Item(i%).Name, “Label”) > 0 Then UserForm1.Controls.Item(i%).TextAlign = fmTextAlignCenter UserForm1.Controls.Item(i%).Font.Size = 20 i% = i% + 1 End If Next element

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

Цикл While в VBA

Цикл while VBA

Циклы в VBA, которые используют структуру Do..Loop (это while и until циклы) можно записывать с разным расположением фрагмента условия. Как видите на картинке выше, условие может проверяться после выполнения одной итерации, а может перед запуском цикла.

Самый популярный пример:

Отсортируйте по возрастанию сгенерированный массив методом пузырька

Dim mas(5) As Integer For i% = 0 To 4 mas(i%) = Int((10 * Rnd) + 1) Next i Dim count As Integer, temp As Integer count = 1 Do While count > 0 count = 0 For i% = 0 To 3 If mas(i) > mas(i + 1) Then temp = mas(i) mas(i) = mas(i + 1) mas(i + 1) = temp count = count + 1 End If Next i% Loop

В вышеуказанном примере мы отсортировали массив с рандомными значениями в порядке возрастания. Метод пузырька считается достаточно долгим, но простым в реализации. В основном, им сортируют небольшие числовые массивы.

Цикл Until в VBA

Цикл Until VBA

Как видите, отличия от while крайне несущественные. Цикл Until в VBA можно реализовать с помощью конструкции while NOT (condition). Тем не менее, приведу пример:

Заставить пользователя ввести число

Dim temp As Variant Do temp = InputBox(“Введите число”) Loop Until IsNumeric(temp)

Почему заставить? Потому, что если пользователь закроет окно ввода, это его не спасёт, оно будет появляться вновь и вновь пока он не введёт любое число.

На этом мы закончим. Сегодня мы рассмотрели важную тему циклов в VBA, разобрали основные примеры. Конечно все примеры по этой обширной теме сложно разобрать, но, тем не менее, основы вы должны понять. Оставляйте ваши комментарии, если у вас возникли вопросы.

Скачать исходник

Источник

Пользователь

Сообщений: 90 Регистрация: 30.08.2013

Привет всем

Простой вопрос по VBA: оператор With…End With (в общем случае) ускоряет выполнение кода или служит просто для удобства при написании?

Пользователь

Сообщений: 10514 Регистрация: 21.12.2012

ускоряет написание кода. На выполнение вряд ли сказывается. ЯТД.

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

Не задавался таким вопросом – думаю в любом случае разница будет незначительной.

Но вот буквы кода точно сокращает

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

Пользователь

Сообщений: 14868 Регистрация: 21.12.2012

и реквизиты в профиле.

Сказывается. Указатель на объект создается один раз, вместо нескольких.

Я сам – дурнее всякого примера! …

Пользователь

Сообщений: 90 Регистрация: 30.08.2013

Спасибо за ответы

Пользователь

Сообщений: 5192 Регистрация: 16.01.2013

а вот, кстати, интересно, а где хранится этот объект? это ссылка?

ZVI

Пользователь

Сообщений: 4183 Регистрация: 23.12.2012

#7

14.09.2013 06:52:14

With … End With ускоряет код, и тем больше, чем больше вложенных объектов (в виде разделительных точек) охватывает, и чем более медленный доступ к этим объектам.

Вот чисто тестовый, но наглядный пример для сравнения:

Код
Sub Test1() Const N = 100000 Dim i&, t!, x t = r For i = 1 To N x = ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas.Count Next Debug. r – t End Sub Sub Test2() Const N = 100000 Dim i&, t!, x t = r With ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas For i = 1 To N x = .Count Next End With Debug. r – t End Sub

Отличие в скорости – в 100 раз.

Указатель, на который ссылается With, записывается в стековый регистр процессора, доступ к которому очень быстрый. Но по этой же причине не рекомендуется из With … End With выходить принудительно по GoTo, так как указатель на объект остается в стеке, а сам объект не удаляется из памяти, т.е. образуется утечка памяти.

Изменено: ZVI – 14.09.2013 06:56:03

Читайте также:  Выход из цикла до его завершения

Пользователь

Сообщений: 5192 Регистрация: 16.01.2013

Пользователь

Сообщений: 12152 Регистрация: 15.09.2012

Профессиональная разработка приложений для MS Office

Владимир – очередной раз спасибо. Не знал про стековый регистр. Знал, что быстрее доступ(опытным путем), но не знал причину.

Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы…

Пользователь

Сообщений: 5192 Регистрация: 16.01.2013

ну.. стековый регистр – это если цикл какой-то немаленький, тогда почувствуешь..

а вот как внутри with .. end with обратиться к самому обекту, им обозначенному?

Hugo

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

#11

14.09.2013 13:17:45

Владимир, мне кажется не корректное сравнение.

Вот то же самое без With – и скорость вообще не отличается:

Код
Sub Test3() Const N = 100000 Dim i&, t!, x, y& t = r Set x = ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas For i = 1 To N y = x.Count Next Debug. r – t End Sub
KuklP

Пользователь

Сообщений: 14868 Регистрация: 21.12.2012

и реквизиты в профиле.

#12

14.09.2013 13:29:28

Игорь, ну ты чего? Ты просто по-другому установил указатель на объект

Яйца те же, только в профиль

P.S. Лирика: в ранней молодости, начиная заниматься радиоконструированием, я скрупулезно вникал, что такое электронно-дырочные переходы и прочее подобное. Дальше в жизни оно мне никак не понадобилось. Надо было просто знать, что подавая смещение на базу транзистора, мы регулируем степень его открытости. И незачем тут лезть в физику процесса, я ее никак не изменю. Законы природы.

pps сравни:

Код
Sub Test3() Const N = 100000 Dim i&, t!, x, y& t = r ‘ Set x = ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas For i = 1 To N y = ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas.Count Next Debug. r – t End Sub

Изменено: KuklP – 14.09.2013 13:33:11

Я сам – дурнее всякого примера! …

Hugo

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

#13

14.09.2013 13:37:42

Я так думаю – задавая with мы один раз определяем этот объект, затем обращаемся к нему.

Я сделал это же через переменную.

А вариант

Код
For i = 1 To N y = ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas.Count Next

заставляет N раз определять этот объект. Неравноценное соревнование!

Пользователь

Сообщений: 14868 Регистрация: 21.12.2012

и реквизиты в профиле.

Ты все правильно понимаешь Или 100000 раз вычислять объект, или использовать один раз вычисленный указатель.

Я сам – дурнее всякого примера! …

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

Так тема о чём? Что быстрее – указатель через set переменная или with без переменной. Я так понял тему.

Пользователь

Сообщений: 14868 Регистрация: 21.12.2012

и реквизиты в профиле.

Гы) Перечитай старттопик.

Я сам – дурнее всякого примера! …

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

Ну так в чём я не прав?

“With…End With (в общем случае)”

Я обычно поступаю так – можно обойтись без переменной – использую with. Нельзя/неудобно – использую переменную.

Замерил – разницы не увидел.

Пользователь

Сообщений: 11745 Регистрация: 22.12.2012

Excel 2016, 365

Поддерживаю Игоря, вариант не совсем корректный.

Предлагаю свой со сменой нескольких свойств шрифта ячейки, выигрыш, правда, не столь впечатляющий.

Время по разным видам

With pCell.Font 2,76

With pCell 2,91

Обращение к pCell.Font.ColorIndex, pCell.Font.Bold, pCell.Font.Italic 2,92

Думаю, если изменять больше свойств шрифта, то разница между будет расти. Тестировал по средним для 6 запусков каждого варианта.

Изменено: anvg – 14.09.2013 15:26:49

KuklP

Пользователь

Сообщений: 14868 Регистрация: 21.12.2012

и реквизиты в профиле.

#19

14.09.2013 16:42:04

Цитата
Так тема о чём? Что быстрее – указатель через set переменная или with без переменной.

Игорь, может мы по разному видим? Я видел вопрос:

Код
оператор With…End With (в общем случае) ускоряет выполнение кода или служит просто для удобства при написании?

Где ты тут увидел сравнение set и with?

Я сам – дурнее всякого примера! …

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

Вот привязался к set…

Ну пусть будет просто – пишем всюду sheets(1).range(… или по возможности .range(…?

Что будет явно и в N раз быстрее?

KuklP

Пользователь

Сообщений: 14868 Регистрация: 21.12.2012

и реквизиты в профиле.

#21

14.09.2013 17:03:09

Я уже не знаю… Игорь, сравни по скорости свой код из 11 поста и он же исправленный из 12. Там мы в каждом проходе цикла вычисляем объект:

Код
ActiveSheet.Cells.SpecialCells(xlCellTypeVisible).Areas

Неужели твой код не быстрей?

Я сам – дурнее всякого примера! …

Hugo

Пользователь

Сообщений: 22988 Регистрация: 22.12.2012

#22

14.09.2013 17:29:39

Я понял про “вычисляем”. Не понял зачем. Как это связано с использованием или неиспользованием with?

Можно ведь было ещё что-нибудь позаковыристей придумать, что-нибудь поглубже в недрах дерева…

А если так погонять:

Код
Sub Test1() Const N = 100000 Dim i&, t!, x t = r For i = 1 To N x = Sheets(1).Index Next Debug. r – t End Sub Sub Test2() Const N = 100000 Dim i&, t!, x t = r With Sheets(1) For i = 1 To N x = .Index Next End With Debug. r – t End Sub

В общем ничего принципиально не изменил….

Да, с with быстрее. Но не всегда.

Источник