Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active March 13, 2018 00:41
Show Gist options
  • Save Jaykul/83ff7b16f3c8a58ca08787c24719afeb to your computer and use it in GitHub Desktop.
Save Jaykul/83ff7b16f3c8a58ca08787c24719afeb to your computer and use it in GitHub Desktop.
Dynamic Parameters Made Easier
function Add-Parameter {
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[Management.Automation.RuntimeDefinedParameterDictionary]
$Dictionary = @{},
[Parameter(Position = 1, Mandatory, ValueFromPipeline)]
[Management.Automation.RuntimeDefinedParameter]$Parameter
)
process {
Write-Debug "Adding $($Parameter.Name): $($PSBoundParameters.Values -join ",")"
$Dictionary.Add($Parameter.Name, $Parameter)
}
end {
$Dictionary
}
}
#.Synopsis
# This example shows something actually useful
function New-Constructor {
[CmdletBinding()]
param($ArgumentList)
dynamicparam {
$CommandName = $MyInvocation.InvocationName
if($CommandName -eq "New-Constructor" -or ($Type = $CommandName -replace "New-" -as [Type]) -eq $null) {
throw "New-Constructor must be called via an alias which represents a type name"
}
$(foreach($property in $Type.GetProperties("Instance,Public,SetProperty")) {
$PType = $(if($property.PropertyType -eq ([System.Boolean])) { [switch] } else { $property.PropertyType })
New-Parameter -Name $property.Name -ParameterType $PType
}) | Add-Parameter
}
end {
New-Object $Type -Property (@{} + $PSBoundParameters)
}
}
# Generate a constructor alias for every Control
[AppDomain]::CurrentDomain.GetAssemblies().GetTypes().Where{
$_.IsPublic -and $_.IsSubClassOf([System.Windows.Forms.Control])
}.Where{
$_.GetConstructors("Public,Instance").Where{ $_.GetParameters().Count -eq 0 }
}.ForEach{ New-Alias ("New-" + $_.FullName) New-Constructor }
# Now to make a TextBox, you can write: "New-*TextBox" and hit {tab} to autocomplete the full type name
# Then you have {tab} completion for all the parameters too!
function New-Parameter {
[OutputType([Management.Automation.RuntimeDefinedParameter])]
[CmdletBinding(DefaultParameterSetName = "Simple")]
param(
[Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName, ValueFromPipeline)]
[String]$Name,
[Parameter(Position = 1, ValueFromPipelineByPropertyName)]
[Alias("Type")]
[Type]$ParameterType = [psobject],
[Parameter(Mandatory, ParameterSetName="Attributes")]
[Attribute[]]
$Attributes,
[Parameter(ParameterSetName="Simple", ValueFromPipelineByPropertyName)]
[String]$ParameterSetName = "__AllParameterSets",
[Parameter(ParameterSetName="Simple", ValueFromPipelineByPropertyName)]
[String[]]$ValidateSet,
[Parameter(ParameterSetName="Simple", ValueFromPipelineByPropertyName)]
[Switch]$Mandatory
)
process {
$param = [Management.Automation.RuntimeDefinedParameter]@{
Name = $Name
ParameterType = $ParameterType
}
if($PSCmdlet.ParameterSetName -eq "Simple") {
Write-Debug "Adding Parameter $Name to $ParameterSetName"
$Attributes = @(
[Parameter]@{
ParameterSetName = $ParameterSetName
Mandatory = $Mandatory
}
if($ValidateSet) {
[ValidateSet]::new($ValidateSet)
}
)
}
foreach($attrib in $Attributes) {
$param.Attributes.Add($attrib)
}
Write-Debug ($param | Out-String)
$param
}
}
#.Synopsis
# This example just shows a few ways to use it
function Test-Example {
[CmdletBinding()]
param()
dynamicparam {
# Because you're a sane person, you wouldn't do this.
# You would only generate dynamic parameters *conditionally*, or from the output of cmdlets
$dP = "File","Command" | New-Parameter -Mandatory -Type Switch -ParameterSet { $_ } | Add-Parameter
$dP = "Path" | New-Parameter -Mandatory -Type String -ParameterSet "File" | Add-Parameter $dP
$dP = "ModuleName" | New-Parameter -Type String -ValidateSet (Get-Module).Name | Add-Parameter $dP
$dP
}
begin {
Write-Host "begin"
}
process {
Write-Host "process"
}
end {
Write-Host "end"
@(
$PSBoundParameters | Format-Table | Out-String -Stream | Where { $_ }
"Path $Path" # this one has a default value
) | Out-Host
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment