Мне нравится, что вы явно ссылаетесь на ActiveSheet
, слава!
Проблема в том, что ActiveSheet
- это Object
, что означает, что компилятор беспомощен:ActiveSheet.Shapes
компилируется, но так же будет ActiveSheet.Shapess
- даже если указано Option Explicit
.Все выражение вычисляется во время выполнения.
Давайте сначала исправим это:
Dim sheet As Worksheet
Set sheet = ActiveSheet
Теперь sheet.Shapes
получает проверку целостности и время компиляции вместе с последующим членом .AddShape
вызов.Вы даже получаете всплывающие подсказки с параметрами при наборе списка аргументов!
Что происходит дальше, интересно: вы объявили MyShape
как Shape
, но это не Shape
, на который вы смотрите -У класса Shape
нет свойства ShapeRange
, так что ... откуда тогда взялся MyShape.ShapeRange
?
Если вы прервете выполнение (F9, чтобы установить точку останова) после вызова MyShape.Select
и затем откройте панель быстрого доступа (Ctrl + G), появится ответ:
?typename(selection)
Rectangle
Если вы нажмете Shift + F2 на слове Rectangle
...
Dim myRectangle As Excel.Rectangle '<~ here
... VBE, похоже, не понимает этого («идентификатор под курсором не распознан»).Поэтому мы нажимаем F2, затем щелкаем правой кнопкой мыши где-нибудь и отмечаем опцию «Показывать скрытых участников» - и, конечно же, вот она:
Итак, ваш код говорит: «Давайте использоватьShape
interface ", но работает с Rectangle
объектом.И поскольку это работает, это означает, что Rectangle
"является" Shape
: два интерфейса просто описывают один и тот же объект через разные линзы, поэтому любой из них работает ... но тогда Shape.ShapeRange
выглядит не совсем правильно, так каккласс Shape
не определяет этот член , и это тот интерфейс, о котором мы прямо говорили, что будем работать с .
Если мы хотим вызвать члены Rectangle
,мы можем - и так как мы теперь показываем скрытые элементы в браузере объектов, intellisense отображает скрытые типы и элементы тоже.Если весь блок With
имеет раннюю привязку, все имеет гораздо больший смысл:
With myRectangle.ShapeRange.Fill
... и объясняет, как код с поздней привязкой из ActiveSheet
будет работать во время выполнения для разрешениявызов члена, и теперь компилятору нужна совершенно другая стратегия для компиляции кода VBA: возможно , которая может встряхнуть все, чтобы заставить его работать, а может и нет.По крайней мере, неоднозначности типов и игнорируемые оператором операторы исчезли:)
Что удивляет, так это то, что вы не можете сделать это с помощью пользовательского кода VBA.Если вы создали класс MyShape
с методом DoSomething
:
'@ModuleDescription "A metaphorical Shape"
Option Explicit
Public Sub DoSomething()
MsgBox TypeName(Me)
End Sub
А затем класс MyRectangle
, который реализует MyShape
и предоставляет член в своем собственном общедоступном интерфейсе, который выдает MyShape
ссылка на объект:
'@ModuleDescription "A metaphorical Rectangle"
Option Explicit
Private sh As MyShape
Implements MyShape
Public Property Get Thing() As Object
Set Thing = sh
End Property
Private Sub Class_Initialize()
Set sh = New MyShape
End Sub
Private Sub MyShape_DoSomething()
MsgBox TypeName(Me)
End Sub
И теперь в любом стандартном модуле мы можем проверить это - сначала все с ранним связыванием, и у нас будет фабричный метод, который возвращает MyShape
для имитацииShapes.CreateShape
:
Public Sub WorksMaybe()
Dim r As MyShape
Set r = CreateRect
r.Thing.DoSomething
End Sub
Private Function CreateRect() As MyShape
Set CreateRect = New MyRectangle
End Function
Итак, мы запускаем это (в Windows), и я ожидал, что код не компилируется:
Позднее связываниеоднако ...
Public Sub WorksMaybe()
Dim r As Object
Set r = CreateRect
r.Thing.DoSomething
End Sub
Private Function CreateRect() As MyShape
Set CreateRect = New MyRectangle
End Function
... работает?Нет:
Разве мы не смотрим на MyRectangle
объект?Нет: мы рассматриваем пределы позднего связывания полиморфизма в VBA - мы создали New MyRectangle
, но компилятору CreateRect
возвращает ссылку на объект MyShape
.Если мы поместим точку останова на End Function
, запустим ее, а затем наберем ?TypeName(CreateRect)
в непосредственной панели (Ctrl + G) при достижении точки останова, несмотря на то, что объявленный тип равен MyShape
, тип времени выполнения явно MyRectangle
.
И это должно работать - но это не так.Ошибка 438, член не найден: поздняя привязка / эквивалент времени выполнения ошибки компиляции «метод или элемент данных не найден».
И если мы используем интерфейс, мы действительно хотим работать с ...
Public Sub WorksMaybe()
Dim r As MyRectangle
Set r = CreateRect
r.Thing.DoSomething
End Sub
Private Function CreateRect() As MyShape
Set CreateRect = New MyRectangle
End Function
... тогда все "просто работает":
Теперь я не запускаю это на Mac, но этот код компилируетсядля меня ...
Option Explicit
Const DistanceBetweenCells As Long = 50
Const LineWidth As Long = 2
Public Sub WorksMaybe()
Dim r As Excel.Rectangle
Set r = CreateRect
r.ShapeRange.Fill.BackColor.RGB = vbRed
End Sub
Private Function CreateRect() As Excel.Shape
Set CreateRect = Shapes.AddShape(msoShapeRectangle, 40, 40, DistanceBetweenCells, LineWidth)
End Function
... и систематически вызывает ошибку времени выполнения 13, как только возвращается CreateRect
и ссылка Shape
назначается на Rectangle
- ошибка 13 является "несоответствием типов".Другими словами, Rectangle
- это , а не a Shape
(!!?! ??).Доказательство: если мы заставим CreateRect
вернуть Excel.Rectangle
, мы теперь получим ошибку несоответствия типов, как только мы попытаемся присвоить возвращаемое значение функции, и больше ничего не имеет смысла: что-то странное происходит, и, ну, яу меня нет идей - кажется, нет никакого способа работать с ранним связыванием с Rectangle
, несмотря на то, что TypeName(Selection)
утверждает, что тип (класс в конце концов скрыт / недокументирован по причине!),что ... в значительной степени разрушает всякую надежду, особенно если ни With Selection.Fill
, ни With MyShape.Fill
не работают (хотя на моем Windows-боксе это прекрасно работает).
Отправка недовольства с помощью некоторого кода репро черезФункция обратной связи с пользователем должна быть услышана от команды разработчиков Microsoft.Я сомневаюсь, что они что-то удалили откуда угодно - но не исключено, что что-то сломалось в том, как разрешаются интерфейсы, где-то глубоко внутри какого-то, казалось бы, не связанного фрагмента внутреннего API:)