0

I'm adding a class related to software packages. Here is a barebones version for the purposes of this question:

class Package {
    $Name; $Latest; $Prerelease
    [string]ToString(){ return $this.Name }
}
$test = [Package]::new()
$test.Name = 'Example'
$test.Latest = '0.1'

When I perform the command $test or $test | Format-Table, I want the $Prerelease property to be hidden unless it's been given a value.

And, while this next part isn't a requirement, I also want it to be exposed when something like Format-Table -Force is used.

I swear I've witnessed other objects in PowerShell behave similarly, but I haven't been able to recall what they were. If this isn't doable for an object made with pure PowerShell, I wouldn't mind something using C# via Add-Type.


P.S. In regards to suggesting an alternative, bear in mind I want quoting the object to output the $Name property as it does in the class I posted above, e.g. "$test"

3
  • Is it a requirement that you use a specific type/custom class? Using PSCustomObject would make this much easier to solve (simply don't add the Prerelease property unless you have a value for it) Commented Jul 29, 2023 at 0:08
  • @MathiasR.Jessen I have considered that, but I want the class to have a ToString() method that outputs $Name, so that when you input "$test" you just get the $Name property. Is that doable with a PSCustomObject? Additionally, I'm wanting this to pipe into another command that would require the class for its InputObject parameter. I could rename the $Name property to something fancy and use ValueFromPipelineByPropertyName instead, but this isn't ideal.
    – Maybe
    Commented Jul 29, 2023 at 0:16
  • Also, I went ahead and added the ToString() bit into my question, to better qualify my needs if there's a good alternative.
    – Maybe
    Commented Jul 29, 2023 at 0:18

1 Answer 1

2

As mentioned in the comments, this is easier achieved by simply using a [PSCustomObject] expression after conditionally adding properties to the target hashtable/dictionary.

You can then add an override for the ToString() method to the resulting object:

function New-VopelPackage {
  param(
    [Parameter(Mandatory = $true)]
    [string]$Name,

    [Parameter(Mandatory = $true)]
    [string]$Latest,

    [Parameter(Mandatory = $false)]
    [switch]$Prerelease
  )

  # Collect the mandatory properties
  $newPackageProperties = [ordered]@{
    Name = $Name
    Latest = $Latest
  }

  # Conditionally add the optional one
  if ($PSBoundParameters.ContainsKey('Prerelease')) {
    $newPackageProperties['Prerelease'] = $Prerelease.IsPresent
  }

  # Create the object
  $package = [PSCustomObject]$newPackageProperties 

  # ... and override ToString()
  $toStringMemberParams = @{
    Name       = 'ToString'
    MemberType = 'ScriptMethod'
    Value      = { return $this.Name }
    Force      = $true
    PassThru   = $true
  }

  return $package |Add-Member @toStringMemberParams
}

Now you can create package objects with the behavior described:

PS ~> $package = New-VopelPackage Example 0.1
PS ~> $package

Name    Latest
----    ------
Example 0.1

PS ~> "$package"
Example
PS ~> $prereleasePackage = New-VopelPackage Example 0.1 -Prerelease
PS ~> $prereleasePackage

Name    Latest Prerelease
----    ------ ----------
Example 0.1          True

PS ~> "$prereleasePackage"
Example
5
  • Thank you. The only thing it's missing is some way for another command to identify that the object it's working with is unmistakably a 'VopelPackage'. Is there a way to add a hidden property to this that it can check for, or something similar?
    – Maybe
    Commented Jul 29, 2023 at 1:11
  • I just discovered PSTypeName, which should fulfill my needs. Thanks for the help.
    – Maybe
    Commented Jul 29, 2023 at 3:23
  • So, I've been using this solution and I'm not completely satisfied. PSTypeName tends to fall off when doing things like piping into Select-Object, which screws up validation. I think there is actually a way to do what I'd like, but it requires C# code and more know-how than I have. I think the key is extending the "INotifyPropertyChanged" class, which I first read about here. I added Console.WriteLine("test"); to OnPropertyChanged to make sure it fires in PowerShell, and it does.
    – Maybe
    Commented Aug 8, 2023 at 16:34
  • (Cont.) So my idea with using this is by having class properties with [HiddenAttribute] from System.Automation and then, whenever one of the attributes is modified for the first time, using AddMember that to add a new ScriptProperty that functions as a getter for it, and the name would be the attribute name plus a space at the end (since PowerShell allows that). If you could lend your expertise, I'd appreciate it.
    – Maybe
    Commented Aug 8, 2023 at 16:40
  • I'm not sure I have enough context here, maybe you'll want to ask another question? Please be specific about what it is you're trying to achieve Commented Aug 8, 2023 at 18:22

Not the answer you're looking for? Browse other questions tagged or ask your own question.