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

Indexer for .NET types that implement IDictionary<TKey, TValue> prevents exceptions for non-existent keys, unlike the underlying type's indexer #4868

Open
mklement0 opened this issue Oct 2, 2019 · 7 comments

Comments

@mklement0
Copy link
Contributor

commented Oct 2, 2019

Probably not too many people will run into this, but it's worth documenting somewhere:

As detailed in PowerShell/PowerShell#10655, PowerShell doesn't call a type's true indexer (implemented as a parameterized .Item property) for types that implement the generic IDictionary<TKey, TValue> interface - instead, when given a key, it quietly tests that key's existence with TryGetValue() and returns $null if it doesn't exist (irrespective of the effective strict mode).

By contrast, if you call the type's true indexer directly, via .Item(<key>), an exception may be thrown (note that in C# using [...] would also throw the exception).

The following snippet illustrates the difference.

PS> [Collections.Generic.Dictionary[string, int]]::new()['nosuchkey']
# No output ($null)

PS> [Collections.Generic.Dictionary[string, int]]::new().Item('nosuchkey')
Exception getting "Item": "The given key 'nosuchkey' was not present in the dictionary."
At line:1 char:1
+ [Collections.Generic.Dictionary[string, int]]::new().Item('nosuchkey' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], GetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenGetting
@sdwheeler

This comment has been minimized.

Copy link
Collaborator

commented Oct 2, 2019

@SteveL-MSFT / @joeyaiello Is this an implementation detail we want to document?

@sdwheeler

This comment has been minimized.

Copy link
Collaborator

commented Oct 2, 2019

This belongs in the SDK docs. Need to find the right place for this information.

@mklement0

This comment has been minimized.

Copy link
Contributor Author

commented Oct 2, 2019

I don't think the SDK docs are the right place, @sdwheeler, because the issue is unrelated to the PowerShell SDK.

The people most likely to be surprised by this behavior are programmers who have experience with working such types in C#.

@sdwheeler

This comment has been minimized.

Copy link
Collaborator

commented Oct 2, 2019

We discussed this in our shiproom meeting with @joeyaiello and @SteveL-MSFT. The behavior of returning null values for properties or keys that don't exist is documented behavior for scripters.

The consensus is that this only applies to C# developers writing PowerShell modules. Module developers need to implement this behavior so that their modules work as expected in PowerShell.

@mklement0

This comment has been minimized.

Copy link
Contributor Author

commented Oct 2, 2019

only applies to C# developers writing PowerShell modules

Agreed (in terms of their expectations), though not just for writing modules - they also need to be aware of this when writing scripts and simple functions - so that they're not surprised by the resulting behavior.

need to implement this behavior so that their modules work as expected in PowerShell.

No, they don't, because the behavior is imposed by PowerShell, so if your code outputs objects that implement IDictionary<TKey, TValue> (regardless of whether the module is implemented in C# or PowerShell), accessing them from PowerShell with [...] will invariably ignore non-existent keys - regardless of what your type does in its true indexer.

behavior of returning null values for properties or keys that don't exist is documented behavior for scripters.

While this may be a common expectation and widely known, I see it documented in neither about_Properties nor in about_Hash_Tables.

However, now that I think about it, the behavior is worth mentioning in about_Hash_Tables.

@mklement0

This comment has been minimized.

Copy link
Contributor Author

commented Oct 7, 2019

To attempt a succinct summary:

  • The issue is one of letting C# developers - more generally, developers who are accustomed to compiled .NET languages - know that PowerShell exhibits different behavior.

  • This is an issue of general awareness that applies to whatever such a developer does - whether writing PowerShell code for themselves or authoring modules, whether in PowerShell or C#. As stated, there are no implementation considerations, because PowerShell applies its different behavior invariably.

  • Aside from a note in about_Hash_Tables - the closest thing to IDictionary<T1,T2> when using PowerShell's standard features - it's worth creating a new conceptual help topic that summarizes all ways in which compiled-.NET-language users may be surprised by PowerShell's behavior, which should additionally include such features such as casts in PowerShell and the (current) inability to call extension methods, and the limitations with respect to calling generic methods.

@sdwheeler

This comment has been minimized.

Copy link
Collaborator

commented Oct 7, 2019

@mklement0 Good discussion. I would welcome a PR on this subject.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.