-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Description
PowerShell makes a hard-coded exception for types that implement the non-generic System.Collections.IDictionary interface with respect to behavior in the pipeline: even though such types are enumerable, PowerShell sends them as a whole through the pipeline.
Source code here:
PowerShell/src/System.Management.Automation/engine/LanguagePrimitives.cs
Lines 523 to 543 in 48c7e11
| private static GetEnumerableDelegate CalculateGetEnumerable(Type objectType) | |
| { | |
| if (typeof(DataTable).IsAssignableFrom(objectType)) | |
| { | |
| return LanguagePrimitives.DataTableEnumerable; | |
| } | |
| // Don't treat IDictionary or XmlNode as enumerable... | |
| if (typeof(IEnumerable).IsAssignableFrom(objectType) | |
| && !typeof(IDictionary).IsAssignableFrom(objectType) | |
| && !typeof(XmlNode).IsAssignableFrom(objectType)) | |
| { | |
| return LanguagePrimitives.TypicalEnumerable; | |
| } | |
| return LanguagePrimitives.ReturnNullEnumerable; | |
| } | |
| private static readonly CallSite<Func<CallSite, object, IEnumerator>> s_getEnumeratorSite = | |
| CallSite<Func<CallSite, object, IEnumerator>>.Create(PSEnumerableBinder.Get()); | |
However, some types only implement the generic equivalent of this interface, System.Collections.Generic.IDictionary<TKey, TValue>.
Such type are therefore unexpectedly enumerated in the pipeline, i.e their key-value pairs are sent rather than the dictionary itself.
Note that while with the IEnumerable / IEnumerable<Type> interface pair implementing the generic version implicitly also implements the non-generic one, this does not apply to the IDictionary / IDictionary<TKey, TValue> pair.
I don't know what the official recommendations are in this case, but at least one notable example of a type that only implements the generic interface is System.Dynamic.ExpandoObject, used in the repro below.
Steps to reproduce
$o = [System.Dynamic.ExpandoObject]::new(); $o.one = 1; $o.two = 2
($o | Measure-Object).Count | Should -Be 1Expected behavior
The test should succeed: the IDictionary2`-implementing type's instance should be sent as a whole through the pipeline.
Actual behavior
The test fails, because the instance is enumerated and its key-value pairs are sent through the pipeline.
InvalidResult: Expected 1, but got 2.
Environment data
PowerShell Core 7.2.0-preview.4