Why does an object returning itself as the default property hang excel and crash the debugger?
I recently came accross `Attribute Values.VB_UserMemId = 0'. I like lists so I thought I'd build a bespoke collection type object.
The minimal code for the class that can reproduce the error is:
Class 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
Essentially, lst(i)
returns the ith element of the private collection, Lst(i)=6
sets the ith element. (errorhandling and index checking code stripped for clarity).
I noticed that objects that return themselves from the default property can be returned from a function in a variant (e.g LstFunc=L
below), without the need for a set
removing complexity from my students eyes...(you cant do that with a collection object)
Unfortunately, I encountered two challenges...the minimum code for these is:
The Problem
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
Here is what happens
1) when the expression L = 6
is evaluated excel hangs. Some times ESC
gets you it back in, but my experience is that excel stops responding and needs a restart.
To evaluate the expression the L.item function is called initially, returning a Lst
, for which item is called, etc.etc. resulting in unwanted, and undetected infinite repetition (not quite recursion). Uncommenting the DoEvents
statement in the get item property allows you to stop without a crash
2) after uncommenting the DoEvents
, I run in debugger mode step by step. If i now hover (by accident..) over the variable L, the debugger crashes, and I get the green triangle of death, which I fear will be very confusing for the students:
Note this behaviour is recoverable if the DoEvents
statement in the class is commented out again. A veritable catch 22...
Bit of an intricate one this, but any sugesstions as to how I can trap the unwanted repetition in (1) at low computational cost and without losing the ability to pass the object like a variant would be greatfully received.
PS this is a code snipped that provides an unsafe workaround discussed in a comment below:
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
excel vba class default
add a comment |
I recently came accross `Attribute Values.VB_UserMemId = 0'. I like lists so I thought I'd build a bespoke collection type object.
The minimal code for the class that can reproduce the error is:
Class 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
Essentially, lst(i)
returns the ith element of the private collection, Lst(i)=6
sets the ith element. (errorhandling and index checking code stripped for clarity).
I noticed that objects that return themselves from the default property can be returned from a function in a variant (e.g LstFunc=L
below), without the need for a set
removing complexity from my students eyes...(you cant do that with a collection object)
Unfortunately, I encountered two challenges...the minimum code for these is:
The Problem
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
Here is what happens
1) when the expression L = 6
is evaluated excel hangs. Some times ESC
gets you it back in, but my experience is that excel stops responding and needs a restart.
To evaluate the expression the L.item function is called initially, returning a Lst
, for which item is called, etc.etc. resulting in unwanted, and undetected infinite repetition (not quite recursion). Uncommenting the DoEvents
statement in the get item property allows you to stop without a crash
2) after uncommenting the DoEvents
, I run in debugger mode step by step. If i now hover (by accident..) over the variable L, the debugger crashes, and I get the green triangle of death, which I fear will be very confusing for the students:
Note this behaviour is recoverable if the DoEvents
statement in the class is commented out again. A veritable catch 22...
Bit of an intricate one this, but any sugesstions as to how I can trap the unwanted repetition in (1) at low computational cost and without losing the ability to pass the object like a variant would be greatfully received.
PS this is a code snipped that provides an unsafe workaround discussed in a comment below:
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
excel vba class default
1
I'm not sure what you mean by "trap the unwanted recursion". Also, I'm not clear on what the purpose of having an object return itself from the default method is. You always have the ability to "pass the object like a variant" in that anything can be stored in a Variant.
– Comintern
Nov 26 '18 at 2:33
Probably should have used 'unintended' infinite repetition caused by evaluation of the p=6 statement as technically it calls L.item repeatedly rather than recursively
– vbAdder
Nov 26 '18 at 8:35
Re the 'returning itself' this is quite distinctly different, note in the code above I work with objects and can treat them as variants, SET is not used at all. you can't return a collection from a function without using Set; one thing less for the students unfamiliar with VBA to get wrong.
– vbAdder
Nov 26 '18 at 8:46
add a comment |
I recently came accross `Attribute Values.VB_UserMemId = 0'. I like lists so I thought I'd build a bespoke collection type object.
The minimal code for the class that can reproduce the error is:
Class 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
Essentially, lst(i)
returns the ith element of the private collection, Lst(i)=6
sets the ith element. (errorhandling and index checking code stripped for clarity).
I noticed that objects that return themselves from the default property can be returned from a function in a variant (e.g LstFunc=L
below), without the need for a set
removing complexity from my students eyes...(you cant do that with a collection object)
Unfortunately, I encountered two challenges...the minimum code for these is:
The Problem
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
Here is what happens
1) when the expression L = 6
is evaluated excel hangs. Some times ESC
gets you it back in, but my experience is that excel stops responding and needs a restart.
To evaluate the expression the L.item function is called initially, returning a Lst
, for which item is called, etc.etc. resulting in unwanted, and undetected infinite repetition (not quite recursion). Uncommenting the DoEvents
statement in the get item property allows you to stop without a crash
2) after uncommenting the DoEvents
, I run in debugger mode step by step. If i now hover (by accident..) over the variable L, the debugger crashes, and I get the green triangle of death, which I fear will be very confusing for the students:
Note this behaviour is recoverable if the DoEvents
statement in the class is commented out again. A veritable catch 22...
Bit of an intricate one this, but any sugesstions as to how I can trap the unwanted repetition in (1) at low computational cost and without losing the ability to pass the object like a variant would be greatfully received.
PS this is a code snipped that provides an unsafe workaround discussed in a comment below:
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
excel vba class default
I recently came accross `Attribute Values.VB_UserMemId = 0'. I like lists so I thought I'd build a bespoke collection type object.
The minimal code for the class that can reproduce the error is:
Class 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
Essentially, lst(i)
returns the ith element of the private collection, Lst(i)=6
sets the ith element. (errorhandling and index checking code stripped for clarity).
I noticed that objects that return themselves from the default property can be returned from a function in a variant (e.g LstFunc=L
below), without the need for a set
removing complexity from my students eyes...(you cant do that with a collection object)
Unfortunately, I encountered two challenges...the minimum code for these is:
The Problem
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
Here is what happens
1) when the expression L = 6
is evaluated excel hangs. Some times ESC
gets you it back in, but my experience is that excel stops responding and needs a restart.
To evaluate the expression the L.item function is called initially, returning a Lst
, for which item is called, etc.etc. resulting in unwanted, and undetected infinite repetition (not quite recursion). Uncommenting the DoEvents
statement in the get item property allows you to stop without a crash
2) after uncommenting the DoEvents
, I run in debugger mode step by step. If i now hover (by accident..) over the variable L, the debugger crashes, and I get the green triangle of death, which I fear will be very confusing for the students:
Note this behaviour is recoverable if the DoEvents
statement in the class is commented out again. A veritable catch 22...
Bit of an intricate one this, but any sugesstions as to how I can trap the unwanted repetition in (1) at low computational cost and without losing the ability to pass the object like a variant would be greatfully received.
PS this is a code snipped that provides an unsafe workaround discussed in a comment below:
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
excel vba class default
excel vba class default
edited Nov 26 '18 at 17:59
vbAdder
asked Nov 26 '18 at 2:19
vbAddervbAdder
1446
1446
1
I'm not sure what you mean by "trap the unwanted recursion". Also, I'm not clear on what the purpose of having an object return itself from the default method is. You always have the ability to "pass the object like a variant" in that anything can be stored in a Variant.
– Comintern
Nov 26 '18 at 2:33
Probably should have used 'unintended' infinite repetition caused by evaluation of the p=6 statement as technically it calls L.item repeatedly rather than recursively
– vbAdder
Nov 26 '18 at 8:35
Re the 'returning itself' this is quite distinctly different, note in the code above I work with objects and can treat them as variants, SET is not used at all. you can't return a collection from a function without using Set; one thing less for the students unfamiliar with VBA to get wrong.
– vbAdder
Nov 26 '18 at 8:46
add a comment |
1
I'm not sure what you mean by "trap the unwanted recursion". Also, I'm not clear on what the purpose of having an object return itself from the default method is. You always have the ability to "pass the object like a variant" in that anything can be stored in a Variant.
– Comintern
Nov 26 '18 at 2:33
Probably should have used 'unintended' infinite repetition caused by evaluation of the p=6 statement as technically it calls L.item repeatedly rather than recursively
– vbAdder
Nov 26 '18 at 8:35
Re the 'returning itself' this is quite distinctly different, note in the code above I work with objects and can treat them as variants, SET is not used at all. you can't return a collection from a function without using Set; one thing less for the students unfamiliar with VBA to get wrong.
– vbAdder
Nov 26 '18 at 8:46
1
1
I'm not sure what you mean by "trap the unwanted recursion". Also, I'm not clear on what the purpose of having an object return itself from the default method is. You always have the ability to "pass the object like a variant" in that anything can be stored in a Variant.
– Comintern
Nov 26 '18 at 2:33
I'm not sure what you mean by "trap the unwanted recursion". Also, I'm not clear on what the purpose of having an object return itself from the default method is. You always have the ability to "pass the object like a variant" in that anything can be stored in a Variant.
– Comintern
Nov 26 '18 at 2:33
Probably should have used 'unintended' infinite repetition caused by evaluation of the p=6 statement as technically it calls L.item repeatedly rather than recursively
– vbAdder
Nov 26 '18 at 8:35
Probably should have used 'unintended' infinite repetition caused by evaluation of the p=6 statement as technically it calls L.item repeatedly rather than recursively
– vbAdder
Nov 26 '18 at 8:35
Re the 'returning itself' this is quite distinctly different, note in the code above I work with objects and can treat them as variants, SET is not used at all. you can't return a collection from a function without using Set; one thing less for the students unfamiliar with VBA to get wrong.
– vbAdder
Nov 26 '18 at 8:46
Re the 'returning itself' this is quite distinctly different, note in the code above I work with objects and can treat them as variants, SET is not used at all. you can't return a collection from a function without using Set; one thing less for the students unfamiliar with VBA to get wrong.
– vbAdder
Nov 26 '18 at 8:46
add a comment |
1 Answer
1
active
oldest
votes
The recursion can't be avoided.
From section 5.6.2.2 of the VBA language specification:
- If the expression’s value type is a specific class:
- If the source object has a public default Property Get or a public default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Note that with your sample class, this line of code meets all of those conditions:
If L = 6 Then DoEvents
The type of the expression L = 6
is Boolean, with an Lst
on the left hand side and an Integer
on the right hand side. That means the type of the comparison is Integer
, so the run-time checks to see if there is a default Property Get
, which you provide here:
Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
The parameter list is compatible with an argument list containing 0 parameters, because the index
is optional. So, it evaluates to L.item() = 6
. The only test you do inside the property is If IsMissing(index)
, which is guaranteed to be true if it's called as the default member - remember, it can't require a parameter to be passed. As you found out, this leads you to...
5.6.2.3 Default Member Recursion Limits
Evaluation of an object whose default Property Get or default function
returns another object can lead to a recursive evaluation process if
the returned object has a further default member. Recursion through
this chain of default members may be implicit if evaluating to a
simple data value and each default member has an empty parameter list,
or explicit if index expressions are specified that specifically
parameterize each default member.
How this is handled is implementation specific. Office VBA implementations, however, do not cap the recursion depth and will simply crash the host when it runs out of stack space.
That said, the rest of your question is simply an x-y problem, although my suggestion is to scrap this. Using default members hides the intent of your code and robust, maintainable code should be readable.
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
|
show 2 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53473985%2fwhy-does-an-object-returning-itself-as-the-default-property-hang-excel-and-crash%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The recursion can't be avoided.
From section 5.6.2.2 of the VBA language specification:
- If the expression’s value type is a specific class:
- If the source object has a public default Property Get or a public default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Note that with your sample class, this line of code meets all of those conditions:
If L = 6 Then DoEvents
The type of the expression L = 6
is Boolean, with an Lst
on the left hand side and an Integer
on the right hand side. That means the type of the comparison is Integer
, so the run-time checks to see if there is a default Property Get
, which you provide here:
Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
The parameter list is compatible with an argument list containing 0 parameters, because the index
is optional. So, it evaluates to L.item() = 6
. The only test you do inside the property is If IsMissing(index)
, which is guaranteed to be true if it's called as the default member - remember, it can't require a parameter to be passed. As you found out, this leads you to...
5.6.2.3 Default Member Recursion Limits
Evaluation of an object whose default Property Get or default function
returns another object can lead to a recursive evaluation process if
the returned object has a further default member. Recursion through
this chain of default members may be implicit if evaluating to a
simple data value and each default member has an empty parameter list,
or explicit if index expressions are specified that specifically
parameterize each default member.
How this is handled is implementation specific. Office VBA implementations, however, do not cap the recursion depth and will simply crash the host when it runs out of stack space.
That said, the rest of your question is simply an x-y problem, although my suggestion is to scrap this. Using default members hides the intent of your code and robust, maintainable code should be readable.
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
|
show 2 more comments
The recursion can't be avoided.
From section 5.6.2.2 of the VBA language specification:
- If the expression’s value type is a specific class:
- If the source object has a public default Property Get or a public default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Note that with your sample class, this line of code meets all of those conditions:
If L = 6 Then DoEvents
The type of the expression L = 6
is Boolean, with an Lst
on the left hand side and an Integer
on the right hand side. That means the type of the comparison is Integer
, so the run-time checks to see if there is a default Property Get
, which you provide here:
Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
The parameter list is compatible with an argument list containing 0 parameters, because the index
is optional. So, it evaluates to L.item() = 6
. The only test you do inside the property is If IsMissing(index)
, which is guaranteed to be true if it's called as the default member - remember, it can't require a parameter to be passed. As you found out, this leads you to...
5.6.2.3 Default Member Recursion Limits
Evaluation of an object whose default Property Get or default function
returns another object can lead to a recursive evaluation process if
the returned object has a further default member. Recursion through
this chain of default members may be implicit if evaluating to a
simple data value and each default member has an empty parameter list,
or explicit if index expressions are specified that specifically
parameterize each default member.
How this is handled is implementation specific. Office VBA implementations, however, do not cap the recursion depth and will simply crash the host when it runs out of stack space.
That said, the rest of your question is simply an x-y problem, although my suggestion is to scrap this. Using default members hides the intent of your code and robust, maintainable code should be readable.
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
|
show 2 more comments
The recursion can't be avoided.
From section 5.6.2.2 of the VBA language specification:
- If the expression’s value type is a specific class:
- If the source object has a public default Property Get or a public default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Note that with your sample class, this line of code meets all of those conditions:
If L = 6 Then DoEvents
The type of the expression L = 6
is Boolean, with an Lst
on the left hand side and an Integer
on the right hand side. That means the type of the comparison is Integer
, so the run-time checks to see if there is a default Property Get
, which you provide here:
Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
The parameter list is compatible with an argument list containing 0 parameters, because the index
is optional. So, it evaluates to L.item() = 6
. The only test you do inside the property is If IsMissing(index)
, which is guaranteed to be true if it's called as the default member - remember, it can't require a parameter to be passed. As you found out, this leads you to...
5.6.2.3 Default Member Recursion Limits
Evaluation of an object whose default Property Get or default function
returns another object can lead to a recursive evaluation process if
the returned object has a further default member. Recursion through
this chain of default members may be implicit if evaluating to a
simple data value and each default member has an empty parameter list,
or explicit if index expressions are specified that specifically
parameterize each default member.
How this is handled is implementation specific. Office VBA implementations, however, do not cap the recursion depth and will simply crash the host when it runs out of stack space.
That said, the rest of your question is simply an x-y problem, although my suggestion is to scrap this. Using default members hides the intent of your code and robust, maintainable code should be readable.
The recursion can't be avoided.
From section 5.6.2.2 of the VBA language specification:
- If the expression’s value type is a specific class:
- If the source object has a public default Property Get or a public default function, and this default member’s parameter list is
compatible with an argument list containing 0 parameters, the simple
data value’s value is the result of evaluating this default member as
a simple data value.
Note that with your sample class, this line of code meets all of those conditions:
If L = 6 Then DoEvents
The type of the expression L = 6
is Boolean, with an Lst
on the left hand side and an Integer
on the right hand side. That means the type of the comparison is Integer
, so the run-time checks to see if there is a default Property Get
, which you provide here:
Public Property Get item(Optional index)
'Attribute Values.VB_UserMemId = 0
The parameter list is compatible with an argument list containing 0 parameters, because the index
is optional. So, it evaluates to L.item() = 6
. The only test you do inside the property is If IsMissing(index)
, which is guaranteed to be true if it's called as the default member - remember, it can't require a parameter to be passed. As you found out, this leads you to...
5.6.2.3 Default Member Recursion Limits
Evaluation of an object whose default Property Get or default function
returns another object can lead to a recursive evaluation process if
the returned object has a further default member. Recursion through
this chain of default members may be implicit if evaluating to a
simple data value and each default member has an empty parameter list,
or explicit if index expressions are specified that specifically
parameterize each default member.
How this is handled is implementation specific. Office VBA implementations, however, do not cap the recursion depth and will simply crash the host when it runs out of stack space.
That said, the rest of your question is simply an x-y problem, although my suggestion is to scrap this. Using default members hides the intent of your code and robust, maintainable code should be readable.
answered Nov 26 '18 at 3:06
CominternComintern
18.7k42456
18.7k42456
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
|
show 2 more comments
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
I haven't looked at OP's code in details, but I believe such a recursive default member implementation can also crash the locals debugger toolwindow.
– Mathieu Guindon
Nov 26 '18 at 3:37
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@MathieuGuindon I'm guessing that it would - I haven't loaded up the OP's class in the VBE, but I'd be surprised if it didn't exhibit the same behavior as hovering over the variable in the debugger.
– Comintern
Nov 26 '18 at 3:39
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
@above two gents, much appreciated you response time! Note the behaviour is different, and depends on whether the doevent statement is uncommented or not.
– vbAdder
Nov 26 '18 at 8:41
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
I now do have a 'solution', I added a static counter to the get item property, and return a string if the counter goes over 1000; counter is set to zero if an index was provided to reset. So as long as L(1) is called once in every 999 L() calls, it will work. Note this will fail if we add 1001 items to the list.... something better would be nice!
– vbAdder
Nov 26 '18 at 8:51
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
@comintern I looked at what is mend by XY-problem :-) agree there is an element of that, though the response of the debugger to the introduction of the Doevent statement has not been clarified.
– vbAdder
Nov 26 '18 at 18:06
|
show 2 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53473985%2fwhy-does-an-object-returning-itself-as-the-default-property-hang-excel-and-crash%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
I'm not sure what you mean by "trap the unwanted recursion". Also, I'm not clear on what the purpose of having an object return itself from the default method is. You always have the ability to "pass the object like a variant" in that anything can be stored in a Variant.
– Comintern
Nov 26 '18 at 2:33
Probably should have used 'unintended' infinite repetition caused by evaluation of the p=6 statement as technically it calls L.item repeatedly rather than recursively
– vbAdder
Nov 26 '18 at 8:35
Re the 'returning itself' this is quite distinctly different, note in the code above I work with objects and can treat them as variants, SET is not used at all. you can't return a collection from a function without using Set; one thing less for the students unfamiliar with VBA to get wrong.
– vbAdder
Nov 26 '18 at 8:46