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
Setting property values with the .ForEach() array method doesn't work with a single [pscustomobject] instance. #16666
Comments
strange, with single brackets does not work but double do @(($obj = [pscustomobject] @{ p = 1 })).ForEach('p', 42)
$obj
p
-
42 |
@scriptingstudio, good find; it's the use of In other words: it's only when calling |
I thought |
@jhoneill just a couple of doc examples https://mcpmag.com/articles/2015/12/02/where-method-in-powershell.aspx, https://powershellmagazine.com/2014/10/22/foreach-and-where-magic-methods
|
Yes, but Count works for scalar and foreach()/where() too. |
They're (now) documented in about_Arrays and briefly mentioned in about_Intrinsic_Members In the former topic they're under the heading "Methods of Arrays", so I've been calling them array methods on Stack Overflow for a while. Yes, they work on scalars too - as they should, given PowerShell's unified handling of scalars and collections (just like you can send a scalar as-is through the pipeline): (1).ForEach({ $_ }) # -> 1
(1).Where({ $true }) # -> 1 (What makes for a slightly awkward asymmetry, however, is that - unlike in the pipeline - the return value is always a collection, namely of type |
Looks like the binder is calling Here's the if (((target is int) && (_version == 0)) && ((member is string) && (value is string)))
{
return EnumerableOps.ForEach(
Fake.Dynamic<Func<CallSite, IEnumerator, IEnumerator>>(PSEnumerableBinder.Get())(
Fake.Dynamic<Func<CallSite, object[], IEnumerator>>(PSEnumerableBinder.Get())(
new object[] { target })),
member,
new object[] { value });
} (side note, dunno what's up with the double enumerate there) And here it is for a if ((((_version == 0) && (target is PSObject)) && ((PSObject.Base(target) == target) && (member is string))) &&
(value is string))
{
return PSInvokeMemberBinder.InvokeAdaptedMember(PSObject.Base(target), "ForEach", new object[] { member, value });
} |
Yes I'd found Boe Prox's one which basically says "this is not for scalars", and Kirk's one says ", two new “magic” methods were introduced for collection types" So not scalars either. These are community experts explaining it, not information from the authors.
Yes, @iSazonov I've relied on that for years... I use The syntax Mike's using is in about_arrays (not About_foreach) and there is a note in about_Intrinsic_Members There are certainly some things that are not right ...
The second has turned the scalar into an array. But the first one seems have turned the whole thing inside out. |
Aside from the second one (which appears to be a very bizarre but separate binding bug) that all looks right. |
🎉This issue was addressed in #17213, which has now been successfully released as Handy links: |
Prerequisites
Steps to reproduce
.ForEach()
has an overload that allows setting property values by name and (array of) property values.This works in general, but unexpectedly not with a single
[pscustomobject]
instance (as later discovered, it does work with arrays of[pscustomobject]
instances).Expected behavior
No output, and
$obj.p
should report42
afterwards.Actual behavior
1
- the current property value - is output, and the attempt to assign a new value is ignored.Error details
No response
Environment data
Visuals
No response
The text was updated successfully, but these errors were encountered: