Страницы: 1
RSS
Почему поиск (Range.Find) срабатывает только 1 раз, если возвращать результат поиска в объявленную переменную (as Range)
 
Моё почтение, уважаемые форумчане. Мой вам земной поклон и пожелания здравствовать и процветать.
Будьте добры, просветите невежду, почему макрос, использующий метод "Range.Find", срабатывает только один раз, если объявить переменную, в которую возвращается результат поиска, как "Range", а если оную не объявлять, то поиск отрабатывает полностью?
Спасибо вам, мудрейшие.
поиск отрабатывает полностью

поиск отрабатывает только один раз
Изменено: JayBhagavan - 24.06.2015 17:16:48 (выделил ключевые фразы)

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
.Address не отрабатывает, если написать с.Address то работает.
 
V, спасибо за ответ. Но если возможно, то объясните, почему? (ведь оба макроса идентичные, отличие только в объявленной переменной) Спасибо.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
with помещает в стек значение переменной и далее вы работаете со стеком

видимо при объявлении как вариант, помещается не сам объект, а ссылка на переменную с и, при изменении c внутри with? стек указывает уже на новый объект, и все срабатывает
Живи и дай жить..
 
Слэн, спасибо за объяснение. Попытаюсь понять. :)

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
это размышление, я не настолько эрудирован.. вот придет ZVI..
Живи и дай жить..
 
Цитата
Слэн написал: вот придет ZVI
Жду не дождусь. :)

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Слэн правильно сказал. При объявлении 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
Как видно, переназначение переменной последней границы ничего не дает - цикл все равно идет до заданной верхней границы. Потому как в память заносится значение, заданное до начала цикла. Не как ссылка на саму переменную, а как копия, если можно так выразится. Если точнее - в память записываются значения границ для цикла.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
The_Prist, спасибо за Ваши сообщения. Постараюсь вникнуть в указанные нюансы и осознать их.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Дим, не совсем понятен момент именно с ОБЪЯВЛЕНИЕМ переменной. Какая разница - явно мы сами задали или Excel определил?
 
Юра, я не совсем понял. А при чем здесь объявление? Давай иначе попробую объяснить. Чуть более развернуто и "по правильному".
Т.е. есть наша переменная 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 - 25.06.2015 08:06:11
 
ZVI, спасибо за Ваше участие и развёрнутый ответ - внесли окончательную ясность (по крайней мере для меня) по данному вопросу. Каюсь, английский знаю плохо, справку читаю редко и невнимательно. Пример был взят из справки по "Range.Find Method". Если Вас не затруднит, то порекомендуйте, где можно почитать про другие "моветоны" дабы их в дальнейшем не допускать. Спасибо.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Цитата
JayBhagavan написал: где можно почитать про другие "моветоны"
Вот большая тема про "моветоны" как со стороны пользователей, так и со стороны разработчиков :)
http://www.cyberforum.ru/vba/thread796990.html
 
Казанский, спасибо Вам. Изучаю. :)

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
всегда поражаюсь насколько ответственно( и развернуто) подходит ZVI к ответам даже на самые простые вопросы..
спасибо, Владимир!
Живи и дай жить..
 
Оживляем тему  :)
ZVI, большое спасибо за исследование!

Цитата
ZVI: Из справки по With: Once a With block is entered, object can't be changed
уж не знаю, что они имели ввиду, но при обращении к диапазону (по ссылке через With) он меняется в зависимости от вставки/удаления строк/столбцов, что продемонстрировано в этой теме
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: 1
Наверх