classpowershellconstructoroverloadingconstructor-chaining

Constructor chaining in PowerShell - call other constructors in the same class


I was doing some testing and stumbled upon the following:

You can overload methods in PoShv5 as you wish. If you call the method without parameters, it can internally call the method with parameters, to keep your code non-redundant. I expected this to be also true for constructors.

In this example, the last constructor is working as expected. The other constructors only return objects without set values.

Class car {
    [string]$make
    [string]$model
    [int]$Speed
    [int]$Year

    speedUp (){
        $this.speedUp(5)
    }
    speedUp ([int]$velocity){
        $this.speed += $velocity
    }

    # Constructor
    car () {
        [car]::new('mall', $Null, $null)
    }

    car ([string]$make, [string]$model) {
        [car]::new($make, $model, 2017)
    }

    car ([string]$make, [string]$model, [int]$Year) { 
        $this.make = $make
        $this.model = $model
        $this.Year = $year
    }
}

[car]::new() # returns "empty" car
[car]::new('Make', 'Nice model') # returns also an "empty" one
[car]::new( 'make', 'nice model', 2017) # returns a "filled" instance

Is there a way to fix this? Did I miss something?


Solution

  • To complement Mathias R. Jessen's helpful answer:

    The recommended approach is to use hidden helper methods to compensate for the lack of constructor chaining:

    Class car {
        
        [string]$Make
        [string]$Model
        [int]$Year
    
        speedUp (){
            $this.speedUp(5)
        }
        speedUp ([int]$velocity){
            $this.speed += $velocity
        }
    
        # Hidden, chained helper methods that the constructors must call.
        hidden Init([string]$make)                 { $this.Init($make, $null) }
        hidden Init([string]$make, [string]$model) { $this.Init($make, $model, 2017) }
        hidden Init([string]$make, [string]$model, [int] $year) {
            $this.make = $make
            $this.model = $model
            $this.Year = $year
        }
    
        # Constructors
        car () {
            $this.Init('Generic')
        }
    
        car ([string]$make) {
            $this.Init($make)
        }
    
        car ([string]$make, [string]$model) {
            $this.Init($make, $model)
        }
    
        car ([string]$make, [string]$model, [int]$year) { 
            $this.Init($make, $model, $year)
        }
    }
    
    [car]::new()                          # use defaults for all fields
    [car]::new('Fiat')                    # use defaults for model and year
    [car]::new( 'Nissan', 'Altima', 2015) # specify values for all fields
    

    This yields:

    Make    Model  Year
    ----    -----  ----
    Generic        2017
    Fiat           2017
    Nissan  Altima 2015
    

    Note: