Powershell subclass constructor - Cannot find an overload for "new"

Viewed 939

Powershell: 5.1.1904.1

OS: Windows 10 Pro x64, version 2004

I'm probably missing something incredibly obvious here, but...

I have a base class and a subclass.

class Vehicle {
    [string]$Name
    Vehicle($Name) {
        $this.Name = $Name
    }
}

class Car : Vehicle {
    [int]$NumWheels
    Car($Name, $NumWheels) {
        $this.NumWheels = $NumWheels
    }
}

$testVar = [Car]::new("Ford Fiesta", 4)

When calling the subclass constructor I'm getting the message

Cannot find an overload for "new" and the argument count: "0".

When I remove the class inheritance I can call the constructor. Powershell seems to find it.

When I remove the inheritance and get info on the constructor

[Car]::new

I see

OverloadDefinitions                                   
-------------------                                   
Car new(System.Object Name, System.Object NumWheels) 

When inheritance is specified (Car : Vehicle) I still get

OverloadDefinitions                                   
-------------------                                   
Car new(System.Object Name, System.Object NumWheels) 

Totally unclear why this it seems to see the constructor, but I'm unable to call it when I inherit.

I know I have to be doing something wrong here. :P

Thank you in advance

2 Answers

To complement Doug Maurer's helpful answer:

The ultimate cause of the problem is that your Vehicle class has no default - always argument-less - constructor, which, as Doug points out, is the result of the class having at least one explicitly defined constructor (Vehicle($Name) { ... }). Once you have at least one explicitly defined constructor, you'll have to define an argument-less one explicitly (Vehicle() { ... }) - if needed.

The immediate cause is that the constructor in your derived class, Car, implicitly tries to call an argument-less constructor on the base class, Vehicle, because you haven't specified an explicit base-class constructor call via base(...) as part of the derived-class constructor.

Therefore, the right solution for you is to call the base-class constructor explicitly, namely the one with the $Name parameter:

# Note the call to the base-class constructor via `base`
Car($Name, $NumWheels) : base($Name) {
    $this.NumWheels = $NumWheels
}

This obviates the need to add an argument-less constructor to the base class just for the sake of inheritance.

See also: about_Classes.

The error "no overload with argument count 0" is because there is no default constructor. When you create your own constructor the default constructor is no longer provided for you. The other issue is you are not fulfilling the contract by assigning the name.

Either create a default constructor for [Vehicle]

class Vehicle {
    [string]$Name
    Vehicle(){}
    Vehicle($Name) {
        $this.Name = $Name
    }
}

class Car : Vehicle {
    [int]$NumWheels

    Car($Name, $NumWheels) {
        $this.NumWheels = $NumWheels
        $this.Name = $Name
    }
}

[Car]::new("Ford Fiesta",4)

NumWheels Name       
--------- ----       
        4 Ford Fiesta

or just remove the custom constructor from it

class Vehicle {
    [string]$Name
}

class Car : Vehicle {
    [int]$NumWheels

    Car($Name, $NumWheels) {
        $this.NumWheels = $NumWheels
        $this.Name = $Name
    }
}

[Car]::new("Ford Fiesta",4)

NumWheels Name       
--------- ----       
        4 Ford Fiesta
Related