Skip to content

A dictionary type that only implements the generic IDictionary interface is unexpectedly enumerated in the pipeline #15204

@mklement0

Description

@mklement0

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:

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 1

Expected 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions