Моё почтение, уважаемые форумчане. Мой вам земной поклон и пожелания здравствовать и процветать. Будьте добры, просветите невежду, почему макрос, использующий метод "Range.Find", срабатывает только один раз, если объявить переменную, в которую возвращается результат поиска, как "Range", а если оную не объявлять, то поиск отрабатывает полностью? Спасибо вам, мудрейшие.
поиск отрабатывает полностью
Код
Sub jjj_find_work()
Dim i&, Rng As Range
Set Rng = Worksheets(1).UsedRange
Set c = Rng.Find("find", LookIn:=xlValues)
If Not c Is Nothing Then
With c
firstAddress = .Address
i = 0
Do
i = i + 1
Set c = Rng.FindNext(c)
Loop While Not c Is Nothing And .Address <> firstAddress
End With
End If
MsgBox "finded: " & i
End Sub
поиск отрабатывает только один раз
Код
Sub jjj_find_not_work()
Dim i&, Rng As Range, c As Range
Set Rng = Worksheets(1).UsedRange
Set c = Rng.Find("find", LookIn:=xlValues)
If Not c Is Nothing Then
With c
firstAddress = .Address
i = 0
Do
i = i + 1
Set c = Rng.FindNext(c)
Loop While Not c Is Nothing And .Address <> firstAddress
End With
End If
MsgBox "finded: " & i
End Sub
with помещает в стек значение переменной и далее вы работаете со стеком
видимо при объявлении как вариант, помещается не сам объект, а ссылка на переменную с и, при изменении c внутри with? стек указывает уже на новый объект, и все срабатывает
Слэн правильно сказал. При объявлении With создается ссылка на объект(копия ячейки памяти), которая будет уничтожена только после End With. И если значение поменять внутри With, то конструкция будет все равно обращаться к изначально заданному для конструкции объекту:
Код
Sub Test()
Dim rc As Range
Set rc = Range("A10")
With rc
MsgBox "Адрес объекта 'With': " & .Address
Set rc = Range("A11")
MsgBox "Адрес объекта 'With': " & .Address
MsgBox "Адрес объекта 'rc': " & rc.Address
End With
With rc
MsgBox "Адрес объекта 'With': " & .Address
Set rc = Range("A12")
MsgBox "Адрес объекта 'With': " & .Address
MsgBox "Адрес объекта 'rc': " & rc.Address
End With
End Sub
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
К слову, точно так же работают и циклы For [var] = lbound to ubound
Код
Sub Test2()
Dim lr As Long, lastr As Long
lastr = 10
For lr = 1 To lastr
MsgBox lr
lastr = 5
Next
MsgBox lastr
End Sub
Как видно, переназначение переменной последней границы ничего не дает - цикл все равно идет до заданной верхней границы. Потому как в память заносится значение, заданное до начала цикла. Не как ссылка на саму переменную, а как копия, если можно так выразится. Если точнее - в память записываются значения границ для цикла.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
Юра, я не совсем понял. А при чем здесь объявление? Давай иначе попробую объяснить. Чуть более развернуто и "по правильному". Т.е. есть наша переменная rc. Мы назначили ей диапазон. Применили With rc в этот самый момент With работает как бы с копией ячейки памяти объекта, которую уже нельзя изменить внутри самой конструкции. rc блокируется для изменений. Все изменения, которые мы назначили переменной rc ставятся в очередь и будут применены к переменной только после End With. Вообще при попытке изменения объекта для With должна происходить ошибка. Но она скорее всего обрабатывается самим VBA, ставя вызовы присвоения значения в очередь. Вот здесь я сам сильно не расскажу про внутренние процессы - тут ждем ZVI :-)
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
The_Prist, несомненно Вы правы насчёт того, что есть неявные (по крайней мере для меня) особенности в работе с конструкцией "With", но и Юрий М прав, акцентируя внимание на том, что в обоих макросах используется конструкция "With", но в одном переменная с результатом поиска НЕ объявлена, а во втором объявлена и из-за этого макрос не отрабатывает так, как считается необходимым. Да, я понимаю как обойти этот момент, но мне не ясны причины его возникновения. Давайте сконцентрируемся на том, почему объявление переменной пагубно сказывается на работе макроса с конструкцией "With". Спасибо всем за внимание к данной теме и ваши ответы и очень жду(ём) ZVI. _/\_
ЗЫ "With" удобна и переменные принято объявлять (если я где этого не делаю, то по причине того, что тот, кому пытаюсь помочь, сам должен разобраться с этим моментом), но, вот, такой казус...
Формула массива (ФМ) вводится Ctrl+Shift+Enter Memento mori
Ой-мой. Что-то я этот момент и упустил. Вроде бы как при объявлении переменной под неё жестко резервируется ячейка памяти. И With туда обращается. Если не объявлять - то при Set c, переменной с назначается ссылка на адрес в памяти для Rng и создается копия на эту ссылку с возможностью изменять её. Возможно, в таких случаях With работает не явно с переменной c, а именно со ссылкой на Rng(т.к. указатель из с ведет к нему). Что позволяет изменять эту самую с внутри конструкции. Наверное это и имел ввиду Слэн.
Догадки. Ждем ZVI.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
В принципе, все уже объяснили Слэн и The_Prist. Добавлю немного. Строго говоря, такая конструкция по стилю некорректна:
Код
With X
Set X = …
End With
( в коде сообщения #1 вместо X используется C ) Это такой же моветончик, как внутри цикла For i = 1 To … Next менять переменную цикла i, если нет цели запутать(ся). Из справки по With: Once a With block is entered, object can't be changed. А то, что иногда этот запрет не работает вообще-то аномально и не рекомендуется использовать.
Теперь об объектных переменных. Здесь возможны 2 варианта: 1. Если переменная X типизирована, т.е. в коде явно задекларирован любой тип объектной переменной (Dim X As Range или Dim X As Object т.п.), то в такой переменной записан Long-адрес объекта, для With статическая копия этого адреса записывается в стек, внутри блока With ... End With эта копия адреса не доступна.
2. Переменная Variant представляет из себя 16 байтную структуру. Если X была в коде объявлена переменной Variant-типа (Dim X As Variant или просто Dim X), и ей присваивается, например, объект ячейки: Set X = Range("A1"), то внутри её 16 байтной структуры помечается, что она теперь будет иметь объектный тип, также внутри неё отводится 4 байта для Long-адреса (интерфейса IUnknown) объекта. В стек With заносится ссылка на Variant-переменную, которая ссылается на объект. Ссылку же на объект внутри Variant-переменной X можно переопределить на другой объект изнутри блока With ... End With с помощью Set. Но не нужно так делать.
Вот код, который подтверждает вышеизложенное:
Код
Sub Test()
Dim X As Variant
Dim RngX As Range, ObjX As Object
Dim Rng1 As Range, Rng2 As Range
Set Rng1 = Range("A1")
Set Rng2 = Range("B2")
Debug.Print "StackAddr", "ObjPtr", "VarAddr"
' Объектная переменная Range/Range
Debug.Print "== Range =="
Set RngX = Rng1
With RngX
Debug.Print .Address(0, 0), ObjPtr(RngX), RngX.Address(0, 0)
Set RngX = Rng2
Debug.Print .Address(0, 0), ObjPtr(RngX), RngX.Address(0, 0)
Set RngX = Nothing
Debug.Print .Address(0, 0)
End With
' Объектная переменная Object/Range
Debug.Print "== Object =="
Set ObjX = Rng1
With ObjX
Debug.Print .Address(0, 0), ObjPtr(ObjX), ObjX.Address(0, 0)
Set ObjX = Rng2
Debug.Print .Address(0, 0), ObjPtr(ObjX), ObjX.Address(0, 0)
Set ObjX = Nothing
Debug.Print .Address(0, 0)
End With
' Переменная Variant/Object/Range
Debug.Print "== Variant =="
Set X = Rng1
With X
Debug.Print .Address(0, 0), ObjPtr(X), X.Address(0, 0)
Set X = Rng2
Debug.Print .Address(0, 0), ObjPtr(X), X.Address(0, 0)
Set X = Nothing
On Error Resume Next
Debug.Print .Address(0, 0); " ' <-- ошибка"
If Err Then Debug.Print "error #" & Err.Number, Err.Description
End With
End Sub
Видно, что объект переопределяется изнутри блока With ... End With только при X As Variant. Ошибка в конце кода для Variant подтверждает, что X везде ссылается на один и тот же заданный/переопределенный объект. После уничтожения объекта внутри End … With получаем X = Nothing. Ну и в завершение заглянем в VBA-Help по методу FindNext(After) – переменная After там указана Variant-типа. Как будто подтасовали нам под эту аномалию
ZVI, спасибо за Ваше участие и развёрнутый ответ - внесли окончательную ясность (по крайней мере для меня) по данному вопросу. Каюсь, английский знаю плохо, справку читаю редко и невнимательно. Пример был взят из справки по "Range.Find Method". Если Вас не затруднит, то порекомендуйте, где можно почитать про другие "моветоны" дабы их в дальнейшем не допускать. Спасибо.
Формула массива (ФМ) вводится Ctrl+Shift+Enter Memento mori
Оживляем тему ZVI, большое спасибо за исследование!
Цитата
ZVI: Из справки по With: Once a With block is entered, object can't be changed
уж не знаю, что они имели ввиду, но при обращении к диапазону (по ссылке через With) он меняется в зависимости от вставки/удаления строк/столбцов, что продемонстрировано в этой теме
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄