Почему объект, возвращающий себя в качестве свойства по умолчанию, зависает в Excel и завершает работу отладчика? - PullRequest
0 голосов
/ 26 ноября 2018

Я недавно натолкнулся на `Значения атрибутов. VB_UserMemId = 0 '.Мне нравятся списки, поэтому я подумал, что должен создать объект типа коллекции на заказ.

Минимальный код для класса, который может воспроизвести ошибку:

Класс Lst

Option Explicit

Public c As New Collection

'this is the default property
Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
    If IsMissing(index) Then
        Set item = Me
        'DoEvents
    Else
        item = c(index)
    End If
End Property

Public Property Let item(Optional index, itm)
    If IsMissing(index) Then 'assume itm is list
        If IsObject(itm) Then Set c = itm.c Else c.add itm
    Else
        c.add itm, , index
        c.Remove index + 1
    End If
End Property

По существу, lst(i) возвращаетi-й элемент частной коллекции Lst(i)=6 устанавливает i-й элемент.(Обработка ошибок и проверка индекса кода удалены для ясности).

Я заметил, что объекты, которые возвращают себя из свойства по умолчанию, могут быть возвращены из функции в варианте (например, LstFunc=L ниже), без необходимости set удаления сложности из глаз моих студентов ... (Вы не можете сделать это с объектом коллекции)

К сожалению, я столкнулся с двумя проблемами ... минимальный код для них:

Проблема

Function LstFunc() As Variant
    Dim L As New Lst
    L = 4 'replaces L.item=3
    LstFunc = L 'this is not normally allowed, but desirable (for me!)
End Function

Sub try()
    Dim L As New Lst
    L = LstFunc 'replaces L.item=LstFunc-->L.c: [4]
    L = 3 'L.c: [4,3]
    If L = 6 Then DoEvents
End Sub

Вот что происходит

1), когда выражение L = 6 оценивается как Excel зависает.Иногда ESC возвращает вас обратно, но мой опыт показывает, что Excel перестает отвечать и нуждается в перезагрузке.

Чтобы оценить выражение, сначала вызывается функция L.item, возвращая Lst, для которого вызывается элемент и т. Д.приводя к нежелательному и необнаруженному бесконечному повторению (не совсем рекурсия).Раскомментируя оператор DoEvents в свойстве get item, вы можете без сбоев остановиться

2) после раскомментирования DoEvents я шаг за шагом запускаю режим отладчика.Если я теперь наведу курсор мыши (случайно ...) на переменную L, отладчик зависнет, и я получу зеленый треугольник смерти , который, боюсь, очень запутает студентов: green triangle of death...

Обратите внимание, что это поведение можно восстановить, если оператор DoEvents в классе снова закомментирован.Настоящий улов 22 ...

Немного сложного в этом, но любые предположения относительно того, как я могу поймать нежелательное повторение в (1) при низких вычислительных затратах и ​​без потери способности передавать объект каквариант был бы принят.

PS это код, который предоставляет небезопасный обходной путь, обсуждаемый в комментарии ниже:

Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
    static i
    If IsMissing(index) Then
        Set item = Me
        i=i+1:if i>1000 then item="":exit property
        'DoEvents
    Else
        item = c(index)
        i=0
    End If
End Property

1 Ответ

0 голосов
/ 26 ноября 2018

Рекурсии нельзя избежать.

Из раздела 5.6.2.2 спецификации языка VBA:

  • Если значение выражениятип - это определенный класс:
    • Если исходный объект имеет общедоступное значение по умолчанию Свойство Get или общедоступную функцию по умолчанию, и список параметров этого элемента по умолчанию совместим со списком аргументов, содержащим 0 параметров,значение простого значения данных является результатом оценки этого элемента по умолчанию как простого значения данных.

Обратите внимание, что в вашем примере класса эта строка кода соответствует всемиз этих условий:

If L = 6 Then DoEvents

Тип выражения L = 6 является логическим, с Lst с левой стороны и Integer с правой стороны,Это означает, что тип сравнения равен Integer, поэтому во время выполнения проверяется, есть ли значение по умолчанию Property Get, которое вы указываете здесь:

Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0

Список параметров совместим со списком аргументов, содержащим 0 параметров, поскольку index является необязательным.Таким образом, он оценивается в L.item() = 6.Единственный тест, который вы выполняете внутри свойства, это If IsMissing(index), что гарантированно будет истинным, если он вызывается как элемент по умолчанию - помните, что может 't требует передачи параметра.Как вы узнали, это приводит вас к ...

5.6.2.3 Пределы рекурсии членов по умолчанию

Оценка объектачье значение по умолчанию Свойство Get или функция по умолчанию возвращает другой объект, может привести к рекурсивному процессу оценки, если возвращаемый объект имеет дополнительный элемент по умолчанию.Рекурсия по этой цепочке членов по умолчанию может быть неявной, если вычисляется простое значение данных, и каждый элемент по умолчанию имеет пустой список параметров, или явным, если заданы выражения индекса, которые конкретно параметризуют каждый элемент по умолчанию.

Как это обрабатывается, зависит от реализации.Реализации Office VBA, однако, не ограничивают глубину рекурсии и просто приводят к краху хоста, когда ему не хватает места в стеке.


Тем не менее, остальная часть вашего вопроса - просто xyпроблема , хотя я предлагаю отказаться от этого.Использование членов по умолчанию скрывает цель вашего кода, и надежный, поддерживаемый код должен быть читабельным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...