Страницы: 1
RSS
Повторяющиеся имена в разных областях видимости, VBA, как правильно обратиться к имени по имени
 
Добрый день, коллеги.
Сижу в отладке, симулирую разные ошибки, столкнулся с таким потенциально неприятным моментом.
Есть файл, в нем определены, предположим, два, по случайности, одинаковых, имени.
Первое имеет область видимости лист "Тест1", и называется оно Name1
Второе имеет область видимости Книга, и называется оно тоже Name1

С помощью макроса, который я тут сократил до минимума, делаю следующее:
Код
Sub test_names()
Dim nms As Names, nmeArr, i&
Set nms = ThisWorkbook.Names: If nms.Count = 0 Then Exit Sub
ReDim nmeArr(1 To nms.Count)
For i = 1 To nms.Count
    Debug.Print "Имя: ", nms(i).Name
    nmeArr(i) = nms(i).Name
    Debug.Print "Вызывается: ", nmeArr(i), "Возвращается: ", nms(nmeArr(i)).Name
Next
End Sub

макрос возвращает на первом проходе:
Код
Имя:          Тест1!Name1
Вызывается:   Тест1!Name1   Возвращается:               Тест1!Name1
всё вроде бы правильно.
На втором имени всё хуже:
Код
Имя:          Name1
Вызывается:   Name1         Возвращается:               Тест1!Name1
На практике в массив имена сохраняются из одной книги, плюс делается еще ряд манипуляций, а потом идет обращение из массива к таким же именам в другой книге.
Если все имена различаются - то и проблем нет, какая разница, какая у него область. А если, вдруг, по нелепой случайности имена будут вот так коряво определены - тогда лезет вот такая ошибка.

Собственно вопросы в следующем:
1) как правильно обратиться к имени по имени, имея ввиду разные области видимости? Через Parent?
2) как правильно сохранить такие имена в массиве, с тем, чтобы потом к ним правильно обратиться?
F1 творит чудеса
 
Есть еще ActiveSheet.Names
Проверять:
Код
    Dim h
    On Error Resume Next
    Set h = ActiveSheet.Names("_name")
    If h Is Nothing Then
        Set h = ActiveWorkbook.Names("_name")
    End If
или без On Error Resume Next циклом сначала по именам листа, а потом уже именам книги.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Дмитрий, спасибо! Но, к сожалению, не совсем то, что нужно, или я всё же не так что-то делаю.
Еще изменил и сократил макрос, пытаюсь обратиться к имени с учетом области видимости:
Код
Sub test_names()
Dim nms As Names
Dim i&
Dim wsh As Worksheet
' имена на листах
For Each wsh In ThisWorkbook.Worksheets
    Set nms = wsh.Names
    For i = 1 To nms.Count
        Debug.Print "Имя " & i & ":", nms(i).Name
        Debug.Print "Область видимости:", nms(i).Parent.Name
        Debug.Print "Что видим при обращении по имени:", wsh.Names(nms(i).Name).Name
    Next
Next
Debug.Print vbLf
' теперь имена книги:
Set nms = ThisWorkbook.Names
For i = 1 To nms.Count
        Debug.Print "Имя " & i & ":", nms(i).Name
        Debug.Print "Область видимости:", nms(i).Parent.Name
        Debug.Print "Что видим при обращении по имени:", ThisWorkbook.Names(nms(i).Name).Name
Next
End Sub 
Результат работы вот такой:
Код
Имя 1:        Тест1!Name1
Область видимости:          Тест1
Что видим при обращении по имени:         Тест1!Name1

Имя 1:        Тест1!Name1
Область видимости:          Тест1
Что видим при обращении по имени:         Тест1!Name1
Имя 2:        Name1
Область видимости:          TestMyName.xlsm
Что видим при обращении по имени:         Тест1!Name1
То есть, при том, что я обращаюсь к имени по его же "имени", без всяких промежуточных массивов, причем обращаюсь к нему с полной нотацией через
Код
ThisWorkbook.Names("имя")
Parent у него получается другой.

Я думаю, проблема в том, что в коллекцию Workbook.Names входят все имена, как определенные в книге, так и на листах.
При этом при обращении к Name по "имени" та его часть, которая отвечает за область (т.е. имя листа), отбрасывается (как, собственно, это и выглядит в диспетчере имен - имя указывается без названия области видимости, сама область указана отдельно).

Вот тут и заковыка. Я, например, хочу присвоить при помощи VBA имени Name1 (уровня книги) какое-то значение. Но присвоение происходит первому по списку имени в коллекции Workbook.Names, имеющему такое же имя на уровне листа. Для имен уровня листа такой проблемы нет - там можно распарсить Тест1!Name1 на две части и обращаться из соответствующей коллекции. Но как заставить обратиться к глобальному экземпляру имени, не путая его с именами на листах? Не могу сообразить.
Понятно, что это следствие некорректного создания имен, но всё же хочется понять, как его обойти.
F1 творит чудеса
 
Не совсем понимаю, в чем сложности. Или не понимаю цель в принципе...
Код
Function IsBookName(sName As String)
    Dim h As Name
    For Each h In ActiveWorkbook.Names
        If h.Name = sName Then
            IsBookName = True
            Exit For
        End If
    Next h
End Function
Sub test()
    MsgBox IsBookName("_name")
End Sub
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
может по принципу такого ?
Изменено: JeyCi - 06.11.2014 19:41:20
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
 
Цитата
The_Prist пишет: Не совсем понимаю, в чем сложности.
В вашем примере работает, конечно, спасибо за совет!

Наверное, я всё-таки неудачно объяснил.
Ну вот имеем такую картинку:

Код
Sub test()
Dim h As Name, i&, sName$

' сохраняем имена на лист
For Each h In ActiveWorkbook.Names
    i = i + 1
    Cells(i, 3) = h.Name
    Cells(i, 4) = "'" & h.RefersTo
Next h

' обращаемся к именам по имени (ПРОБЛЕМА ТУТ)
For i = 1 To ActiveWorkbook.Names.Count
    sName = Cells(i, 3)
    Cells(i, 5) = Evaluate(ActiveWorkbook.Names(sName).RefersTo)
Next i

' обращаемся перебором имен (здесь всё правильно)
For i = 1 To ActiveWorkbook.Names.Count
    sName = Cells(i, 3)
    For Each h In ActiveWorkbook.Names
        If h.Name = sName Then
            Cells(i, 6) = Evaluate(h.RefersTo)
            Exit For
        End If
    Next h
Next i

End Sub
в итоге имеем вот такое:


где отмеченное желтым - это то, что мы получили, пытаясь обратиться к имени по сохраненному ранее имени напрямую, не выполняя сравнения h.Name = sName

JeyCi, спасибо за ссылку - много полезного, да  :)  половину перепробовал сам, вторая половина тоже очень полезная, но всё-таки не дает ответ.
Но, совместными усилиями, разобрался.

Придется для каждого сохраненного имени пробегать по всей коллекции, напрямую лучше не обращаться.
Еще раз всем спасибо!
Изменено: Максим Зеленский - 07.11.2014 01:07:14 (файлик с бядой добавил)
F1 творит чудеса
 
я думала что-нибудь вроде этих оттуда могло подойти
Код
If TypeName(iName.Parent) = "Workbook" Then 
'
If TypeName(Evaluate(iName.RefersTo)) = "Range" Then 
If TypeOf Evaluate(iName.RefersTo) Is Range Then
If Evaluate("IsRef(" & Mid(iName.RefersTo, 2) & ")") = True Then
но тоже не совсем поняла для каких целей  8) ... всё равно желаю успехов
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
 
Цитата
Максим Зеленский пишет: '  (ПРОБЛЕМА ТУТ) For i = 1 To ActiveWorkbook.Names.Count sName = Cells(i, 3) ... Next i
может лучше sName = Cstr (Cells(i, 3).Value) ?
Цитата
Максим Зеленский пишет: Но, совместными усилиями, разобрался.
но это тоже хорошо...
Изменено: JeyCi - 07.11.2014 01:07:28
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
 
JeyCi, нет. По сути приведенные Вами коды предназначены для других целей: они проверяют является ли имя ссылкой на диапазон или просто константой. А Максим хотел получить значение имени в области видимости именно "Книга", если таких имен два с разной областью видимости. И по умолчанию, если именно этот лист активный, то будет использовано именно имя с областью видимости "Лист", а не "Книга".

Я понял, что Максим хотел, но не понял зачем это надо...
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
The_Prist пишет: они проверяют является ли имя ссылкой на диапазон или просто константой. А Максим хотел получить значение имени в области видимости именно "Книга
а в чём проблема? если имя глобальное- имеет уровень рабочей книги (1-й If) - то его берём, иначе не нужен... как-то так... ну, нет так нет :) я не спорю
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
 
Цитата
Максим Зеленский пишет: предположим, два, по случайности, одинаковых, имени.
Зачем делать то, что делать не следует. Да, понятно, коллекции разные, но....... зачем потом танцевать с бубном?
Цитата
Максим Зеленский пишет: по случайности
В программировании не может быть случайностей, может быть ошибка программиста.
Изменено: B.Key - 07.11.2014 01:08:07
 
А имя может создать и не программист))
 
Скажем так, я вообще не знаю, какие имена есть в книге, в которой потом будет использоваться код.
Это как раз попытка понять, как обойти некорректно созданные имена, если таковые встретятся - как  раз чтобы не было "случайностей" )) Часть решения другой задачи, где используется список имен, сохраненный отдельно, в другом файле: я их сохранил, потом достаю оттуда и пытаюсь обратиться, и вот такой косяк и нашел.
Цитата
JeyCi пишет: может лучше sName = Cstr (Cells(i, 3).Value) ?
В рамках примера не принципально ))
F1 творит чудеса
 
Цитата
Максим Зеленский пишет: Это как раз попытка понять, как обойти некорректно созданные имена, если таковые встретятся
что значит некорректно?... там по линку сверху вопросы, нажимая на которые - спускает вниз к коду... неудобно отдельно сразу ответы читать - но так построили страничку - что поделать... так может вопрос об ошибке - тогда это вопрос Н... если вопрос об отношении имени к книге или листу - то это вопрос вроде про уровень имени (вопросы сверху, ответы где попало)... а дальше как угодно: хоть If, хоть If not ... но циклом пробежать по именам, конечно, придётся...
вобщем, мне понравился вывод поста№6 о том, что всё хорошо...  :)  я поверю
в любом случае замечательно если ответ найден...
Изменено: JeyCi - 06.11.2014 23:53:11
чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок - обратитесь к собеседнику на ВЫ - ответ на ваш вопрос получите - а остальное вас не касается (п.п.п. на форумах)
 
Некорректно, на мой взгляд - это когда в книге соседствуют два одинаковых имени с разной областью видимости. Лучше, чтобы такого не было, конфликт имен - лишний геморрой.
F1 творит чудеса
 
Одинаковые имена должны пресекаться на уровне самого экселя, имхо. А как побороть Ваш вопрос, увы, не знаю. Как я понял, то Вы пытаетесь сделать защиту от "дурака" в алгоритме своей программы.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Цитата
JayBhagavan пишет: должны пресекаться на уровне самого экселя
ну вот так можно симулировать повторяющиеся имена:
создаем файл с листом Лист1, в нем создаем имя Name1 уровня книги.
создаем еще один пустой файл, в нем также создаем имя Name1 уровня книги. Переименуем Лист1 в Лист2, для наглядности - это не обязательно
копируем лист из первого файла во второй. результат:


копируем внутри второго файла Лист2 (тот, который был внутри этой книги):

и так далее.
Правда, ошибка, с которой я борюсь, будет не всегда. Тоже загадка.
F1 творит чудеса
 
"всё так запутано" (с).
Моё имхо: никогда не создавать именованных формул уровня книги, если это - прямая или косвенная ссылка на диапазон ячеек.
Изменено: С.М. - 07.11.2014 12:00:59
Страницы: 1
Наверх