Powershell: Encourage using IDictionary interface for parameters

Created on 23 Oct 2020  路  3Comments  路  Source: PowerShell/PowerShell

Encourage developers to use the IDictionary interface for the parameter type

(if the [Hashtable] type is not explicitly required which I think it almost always the case)

Currently, In most custom functions, the specific [Hashtable] dictionary type is used for parameter type where it would be better to use the more generic IDictionary interface so that is would accept any ICollection or IEnumerable (as e.g. the [Ordered] type), knowing that the enumeration of all IDictionary Objects are common and most native PowerShell cmdlet accept the IDictionary interface.
The reason that specific [Hashtable] dictionary type is used is because the [Hashtable] type is more known (and default) within PowerShell and has simply a shorter type name.

Proposed technical implementation details (optional)

  • Add the IDictionary interface to the default list of type accelerators:
$TypeAccelerators = [psobject].Assembly.GetType('System.Management.Automation.TypeAccelerators')
$TypeAccelerators::Add('IDictionary', [System.Collections.IDictionary]) # Or just 'Dictionary'?

So that a developer/user can use any dictionary object for input:

Function Test([IDictionary]$Dictionary) {
    [pscustomobject]$Dictionary
}
Test ([Ordered]@{ a = 1; b = 2; c = 3 })

Possibly also:

  • Add the recommendation to use the IDictionary interface to the best practice documentation
  • Create a PSScriptAnalyzer warning when an explicit [Hashtable] type is used for a parameter?

Related:

  • Feature Request: add [Ordered] type accelerator #10513
  • Enhance hash table syntax #13817
Issue-Enhancement WG-Engine

All 3 comments

I think you mean IDictionary? 馃

Currently the engine isn't very smart about casting to interfaces. It's not that big of a deal for IDictionary, but before interfaces are recommended that should probably be solved.

This is more often a problem when it comes to IEnumerable<T> and IList<T>. PowerShell knows how to cast object[] to int[], and it knows that int[] is IEnumerable<int>, but it doesn't know how to cast object[] to IEnumerable<int>. Admittedly, that's not an easy problem to solve, but I do think it's worth it (even if that just means hard coding conversion paths for common interfaces like IEnumerable<> and IDictionary).

I think it's fine to already start recommending use of IDictionary specifically, given that it is both a common use case and works as intended - though better support such as object[] to IEnumerable<int> casts would certainly be nice.

I think the challenges of interface use in PowerShell are primarily _conceptual_:

  • User usually don't have to deal with interfaces directly, given that PowerShell surfaces even explicit interface implementations as direct object members, and less experienced users may even be unaware of the concept of interfaces altogether.

    • This means that when they see interface-based parameter declarations (e.g., IDictionary for New-Object's -Property parameter), they need to either rely on the parameter _description_ to know what concrete types to pass or rely on their own advanced .NET knowledge, including knowing how to inspect a given type (instance) for implementing the target interface.
  • Script authors, especially those with a C# background, need to be aware that PowerShell generally _ignores_ casts to interfaces and base classes and that such casts matter only for .NET method overload resolution and during PowerShell function/script parameter binding, but even in the latter case what the script / function sees as the parameter _variable_ is the _original type_.

    • In other words: in PowerShell, you never interact with an interface _directly_, only with an implementing type's implementations of its members, which means that inside a script with an interface-typed parameter you need to be aware of what members are truly the interface's, as only they can be relied on.

In addition to documentation, the following would also help:

The ability to inspect a given instance or type for the interfaces it implements as well as their members - see #13865

Was this page helpful?
0 / 5 - 0 ratings