-
Notifications
You must be signed in to change notification settings - Fork 288
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
[Question] Advanced functions | Parameter binding by PSTypeName #107
Comments
Duplicating the relevant part of my post from your thread on the PS forum so I can follow this discussion also. :)
|
Well I was looking for a way filling gap of using |
@OCram85 that is exactly what I use them for. As has been said it doesn't do any property validation but it does make sure the caller is passing the correct object (which could be the output from another function of yours). So that is a validation in itself. |
@pauby: That's exactly what I wanted to achieve. But I'm really wondering why this is used so rarely and also not documented ^^ |
Totally agree. I tend to use it for things like configuration data that I need to be sure is in a specific format. But I'm really not sure why it's not used more readily. Its not difficult to do so that's not a stumbling block. As you said the documentation for it is lacking too. I stumbled across it while looking for something else. Its had me wondering if there isn't some horrible consequences of using it that everybody knows except me! It might make a good lightning talk. Hmm. |
Wouldn't best practice nowadays be to use a class instead? Seems a lot clearer and simpler to me. class ContextIdentifier {
$SomePropertyKey
$foo
}
function Get-MyAwesomeCustomType {
[CmdletBinding()]
[OutputType([ContextIdentifier])]
param()
$returnObj = [ContextIdentifier]@{
SomePropertyKey = 'awesome value'
Foo = 'bar'
}
Write-Output $returnObj
}
function Invoke-AwesomeStuff {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[ContextIdentifier]$InputObj
)
Write-Host $InputObj.foo
} |
Assuming what you need can be crafted in a PS class, sure. Otherwise you may need to mess with inlined C# and For simpler objects, I don't see any particular need for a class. For anything decently complex, or especially if you want to be able to work neatly with multiple types of input (which can be done with well-designed constructor overloads), you'll find it much easier to use a class. Also note that your hashtable->class object conversion is only possible on an object where the following two conditions are met:
Setters are really only implemented in C#, but if you're implementing an interface that works with getters and setters, it's still something you need to be aware of, even in PS classes. I'm looking at potentially improving that bit of parser magic in PS Core, but I'm not yet sure how it'll work, precisely. |
I felt the main point of the post was type safety for custom objects in a module. The generic PSCustomObject gives you no real safety, hence the workaround with Anything you can do with For more complex scenarios wouldn't it be better to just write it in C# and compile it? |
The difficulty with using a purely custom class is that oftentimes since these are not exported in most cases from a module (the only way I've found to export them from a module by default is having them in a separate .ps1 script that is referenced in ScriptsToProcess in the PS1 file) there is little to no autocomplete or support for building a proper object from outside the module, at the console prompt, in order to use it to its fullest potential. Unfamiliar types for parameters pose a difficulty in that users may be unsure exactly what kind of input to give -- and answers to that have to be given in documentation on a per-function basis, because there is (as mentioned above) limited support for exploring custom types that a module introduces in and of itself. |
One specific use case for PSTypeNames is when you're adding things to existing objects. For example, I was working last night on a function which has two parameter sets. One of them has a In the passthru case, I insert a PSTypeName so that the object, in addition to still being whatever it was before, is now also one of my objects. This cannot be done with a PowerShell custom class, because I don't want to limit what you can pass through, nor encapsulate what you pass through in a container with a We are using the PSTypeName restriction as a weak stand-in for "looks like a duck" here, and it would be better is if I could write something like: [Parameter()]
[ValidateProperties(@{ SourceFile = [string]; SourceLineNumber = [int]})] |
P.S. @vexx32 the only correct way to define classes and have them exported correctly (in such a way that they live in your module and are scoped correctly) is to put directly them IN the main PSM1 Then, when you want to use them, write: |
As you mention, that does not import custom types when you import the module. Granted, you may not always want to, but say for example you have a custom class to simplify parameter validation / conversions. Suddenly there's an arbitrary (hopefully well-named, but we all know that's not that likely) type name when you If you happen to not know about Custom types are fantastic for parameter validation, but with this barrier to discovery, I'm not a fan of them whatsoever. |
Yep, I should modify my recommendation, because I mispoke earlier. Clearly complex types aren't the best practice ...
If you need a lot of parameters and can easily get their values from another command, you will want to pipeline the object. In this case, the best way is clearly to ensure it's properties are simple value types, and then accept those simple values as parameters, using However, it is obviously acceptable to use custom types as a parameter. In particular, it's common to do the many parameters thing I mentioned and also have an In those cases, the best practice is going to be to accept strongly typed objects defined with either .Net or PowerShell Classes, and the next best would be to use a PSTypeName restriction. As additional suggestions:
I'm sure I'm missing a few restrictions, and obviously there are always exceptions to the rules ... |
Fully agree with that recommendation. Wanna copy-paste that into the relevant section in the guide? 😉 |
Hi guys,
I recently discovered the possibility to bind a parameter by a custom type name. The common approach seems to be using
[PSCustomObject]
as parameter type if you don't use classes.Just for clarify a little example:
My questions here are:
The text was updated successfully, but these errors were encountered: