PS C:> $h = @{}
PS C:> $h.Add('foo', 42)
PS C:> $h.Add('bar', 42)
PS C:> $h.Keys
bar
foo
PS C:> $h.Add('keys', 42)
PS C:> $h.Keys
42
PS C:> $h
Name Value
---- -----
bar 42
foo 42
keys 42
The Hashtable's .Keys property should always return the KeyCollection value from the underlying Hashtable. (Note: this same issue occurs with .Values, .Count, etc.)
The convenience feature of accessing a Hashtable's entries with property syntax hides the real properties.
Other collection types are not effected, for example, substituting the following works:
$h = [System.Collections.Generic.Dictionary[string,int]]::new()
Name Value
---- -----
PSRemotingProtocolVersion 2.3
PSVersion 6.0.0-alpha
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
SerializationVersion 1.1.0.1
PSEdition Core
BuildVersion 3.0.0.0
CLRVersion
GitCommitId v6.0.0-alpha.16
WSManStackVersion 3.0
This would be a breaking change. Workaround is
$h.psbase.keys
I suspected it might be too late to change this behavior. It sure did lead to a subtle bug in my case - as the hashtable was being populated with words from user supplied text. Perhaps a documentation update?
Thank you for the more general workaround - .PSBase is one of those tricks I know but forget about.
Agree on documentation, I just submitted this PR: https://github.com/PowerShell/PowerShell-Docs/pull/1065
The problem does apply to any of the聽native properties, so Values and Count are both an issue as well (and for completeness IsFixedSize, IsReadOnly, IsSynchronized, and SyncRoot but there's unlikely to be much PowerShell code actually trying to read any of those).
I updated the PR to make it more generic about any collision
Yes, I think that covers it聽perfectly. Thanks again.
@bcdev-com Thanks for reporting this!
The .psbase workaround is handy - thanks, @SteveL-MSFT.
For a related discussion with more background information, see #7758