Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.Array+SZArrayEnumerator Count check boots out of foreach loop #13607

Closed
ParadoxGBB opened this issue Sep 9, 2020 · 4 comments
Closed
Labels
Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered.

Comments

@ParadoxGBB
Copy link

ParadoxGBB commented Sep 9, 2020

The following script apparently boots out of the inner foreach loop and I can't come up with a reason why. The intent is to iterate through all variables in the current scope and do customized handling for those that are collections.

#REPRO BEGIN

foreach($currentIteration in @("one"))
{
	#ONE OF THESE VARIABLES IS NOW "FOREACH"
	$allVars = Get-Variable -Scope 0

	Write-Host Total Variable Count: [($allVars.Count)]
	$cVar = 0

	foreach($currentVariable in $allVars)
	{
		$cVar++
		if($currentVariable.Value -eq $null)
		{
			Write-Host Variable: [$cVar] [($currentVariable.Name)] [NULL]
		}
		else
		{
			Write-Host Variable: [$cVar] [($currentVariable.Name)] [($currentVariable.Value.GetType().ToString())]
		}

		#THIS CAUSES A BREAK FOR "FOREACH" VARIABLE
		if($currentVariable.Value.Count -gt 1)
		{
			Write-Host Collection
		}
	}

	Write-Host Total Variables Iterated: [$cVar]
}

Expected behavior

List all 28 variables in scope, output similar to the below (or in other words, $cVar -eq $allVars.Count):

PS > .\testscript.ps1
Total Variable Count: [ 28 ]
Variable: [1] [ ? ] [ System.Boolean ]
Variable: [2] [ currentIteration ] [ System.String ]
Variable: [3] [ EnabledExperimentalFeatures ] [ System.Management.Automation.Internal.ReadOnlyBag`1[System.String] ]
Variable: [4] [ ExecutionContext ] [ System.Management.Automation.EngineIntrinsics ]
Variable: [5] [ false ] [ System.Boolean ]
Variable: [6] [ foreach ] [ System.SZArrayEnumerator ]
.
.
.
Variable: [28] ...
Total Variables Iterated: [28]

Actual behavior

PS > .\testscript.ps1
Total Variable Count: [ 28 ]
Variable: [1] [ ? ] [ System.Boolean ]
Variable: [2] [ currentIteration ] [ System.String ]
Variable: [3] [ EnabledExperimentalFeatures ] [ System.Management.Automation.Internal.ReadOnlyBag`1[System.String] ]
Variable: [4] [ ExecutionContext ] [ System.Management.Automation.EngineIntrinsics ]
Variable: [5] [ false ] [ System.Boolean ]
Variable: [6] [ foreach ] [ System.SZArrayEnumerator ]
Total Variables Iterated: [6]

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.3
PSEdition                      Core
GitCommitId                    7.0.3
OS                             Microsoft Windows 10.0.19042
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

@ParadoxGBB ParadoxGBB added the Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a label Sep 9, 2020
@vexx32
Copy link
Collaborator

vexx32 commented Sep 9, 2020

$foreach has no .Count property natively, so I'm assuming it causes the enumerator to be iterated to find said count value, similar to how you can iterate an array with $array.Property to look at all the .Property values in the array. This causes the iterator to be accelerated to the end of the loop entirely as soon as you call .Count on it.

Basically what you're triggering is the same as what this will do:

foreach ($a in 1..10) {
    Write-Host $a
    $foreach.MoveNext()
}

Except instead of advancing one additional time (skipping values, in effect), you're iterating the whole thing by accident and the next time the loop tries to iterate, it's just told "we reached the end, exit the loop".

@ParadoxGBB
Copy link
Author

Thanks for the response. That's a better explanation than what I was able to come up with --- but if that's the case, that's a pretty funky side-effect of calling System.SZArrayEnumerator.Count and is probably never the intention.

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented Sep 10, 2020

that's a pretty funky side-effect of calling System.SZArrayEnumerator.Count and is probably never the intention.

A bit of a niche scenario for sure, but I would say the example above is as well. Special casing enumerators here would technically be a bucket 1 breaking change (though probably lower impact than that implies), and probably a very small performance hit in the binder. I don't think it's likely to be changed unless you have a few high impact scenarios you can share.

@iSazonov iSazonov added the Resolution-Answered The question is answered. label Sep 10, 2020
@msftbot
Copy link

msftbot bot commented Sep 11, 2020

This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.

@msftbot msftbot bot closed this as completed Sep 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered.
Projects
None yet
Development

No branches or pull requests

4 participants