Overload by type in Powershell constructor?

Viewed 349

I'm wondering if it's possible to create a class in Powershell with two constructors, both of which accept the same number of parameters, but they'd be of different types. For example, I want to create something like the following:

class User {
    [string]$GivenName
    [string]$Surname
    [string]$DisplayName
    [string]$Company
    [string]$PayrollNo
    [string]$Email
    [DateTime]$StartDate
    [DateTime]$LeaveDate

    User([Microsoft.ActiveDirectory.Management.ADAccount]$ADUser) {
        $this.GivenName   = $ADUser.GivenName
        $this.Surname     = $ADUser.Surname
        $this.DisplayName = $ADUser.Name
        $this.PayrollNo   = $ADUser.EmployeeID
        $this.Email       = $ADUser.EmailAddress
    }

    User([System.Array]$TraceUser) {
        $this.GivenName   = $TraceUser.'First Name'
        $this.Surname     = $TraceUser.Surname
        $this.DisplayName = "$($TraceUser.'First Name') $($TraceUser.Surname)"
        $this.Company     = $TraceUser.'Payroll Company'
        $this.PayrollNo   = $TraceUser.'Payroll No'
        $this.Email       = $TraceUser.'E-Mail'
        $this.StartDate   = [datetime]::ParseExact("$($TraceUser.'Start Date') 23:59",'dd/MM/yyyy HH:mm',$null)
        $this.LeaveDate   = [datetime]::ParseExact("$($TraceUser.'Leaving Date') 23:59",'dd/MM/yyyy HH:mm',$null)
    }
}

Where if I instantiate an object with an AD user as a parameter, it'll use the first constructor, but if I instantiate with an array, it'll use the second one.

This doesn't seem to be working, and all I can find by searching online is how to overload by using different numbers of arguments. Nothing about overloading based on type.

Is this possible?

EDIT: To give some more context, ArcSet below guessed my error correctly. I'm seeing:

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

The $ADUser that's passed to the first constructor is obtained by running Get-ADUser. The $TraceUser that's passed to the second constructor comes from an Import-CSV.

When I instantiate an object of the User class, I'm going to call it either with an AD User from Get-ADUser, in which case I want the first constructor to run, or I'm going to call it with a System.Array that's come from Import-CSV, in which case I want the second constructor to run.

I'm actually instantiating these within ForEach loops, but I don't think that's relevant here. Below are examples of how I'm creating these:

$oneADUser      = Get-ADUser -Filter * -SearchBase $OU
$oneTraceUser   = Import-CSV somefile.csv
$NewADUsers     = @()
$NewTraceUsers  = @()
$NewADUsers    += [User]::new($oneADUser)    # This should use the first constructor
$NewTraceUsers += [User]::new($oneTraceUser) # This should use the second constructor

Am I doing this right?

2 Answers

I think the Error you are probably getting is

Cannot find an overload for "User" and the argument count: "{Some Number Here like 85}"

That is because it is treating the array you are passing as a array of arguments

New-Object User -ArgumentList (Get-ADUser -Filter * | select -First 3)

This will fail with the message

Cannot find an overload for "User" and the argument count: "3"**

How to get around that is put a , before it

New-Object User -ArgumentList (,(Get-ADUser -Filter * | select -First 3))

You can also use ::New()

[User]::new((Get-ADUser -Filter * | select -First 3))

Ok, I figured this out. Thanks everyone for your help.

To get this to work, I had to cast $oneTraceUser to a System.Array before passing it to the constructor. Final working code looks like this:

class User {
    [string]$GivenName
    [string]$Surname
    [string]$DisplayName
    [string]$Company
    [string]$PayrollNo
    [string]$Email
    [DateTime]$StartDate
    [DateTime]$LeaveDate

    User([Microsoft.ActiveDirectory.Management.ADAccount]$ADUser) {
        $this.GivenName   = $ADUser.GivenName
        $this.Surname     = $ADUser.Surname
        $this.DisplayName = $ADUser.Name
        $this.PayrollNo   = $ADUser.EmployeeID
        $this.Email       = $ADUser.EmailAddress
        #$this.LeaveDate  = $ADUser.AccountExpires
    }

    User([System.Array]$TraceUser) {
        $this.GivenName   = $TraceUser.'First Name'
        $this.Surname     = $TraceUser.Surname
        $this.DisplayName = "$($TraceUser.'First Name') $($TraceUser.Surname)"
        $this.Company     = $TraceUser.'Payroll Company'
        $this.PayrollNo   = $TraceUser.'Payroll No'
        $this.Email       = $TraceUser.'E-Mail'
        $this.StartDate   = [datetime]::ParseExact("$($TraceUser.'Start Date') 23:59",'dd/MM/yyyy HH:mm',$null)
        $this.LeaveDate   = [datetime]::ParseExact("$($TraceUser.'Leaving Date') 23:59",'dd/MM/yyyy HH:mm',$null)
    }
}

$NewADUsers = New-Object -TypeName "System.Collections.ArrayList"
$NewTraceUsers = New-Object -TypeName "System.Collections.ArrayList"
$TraceData = Import-CSV $TraceCSV
$ADUsers = Get-ADUser -Filter * -SearchBase $OU -Properties EmployeeID
$TraceUsers = $TraceData | Where-Object 'Payroll Company' -eq $OU.DestinationIndicator
forEach($oneADUser in $ADUsers) {
    $NewADUser = [User]::new($oneADUser)
    forEach($oneTraceUser in $TraceUsers) {
        $NewTraceUser = [User]::new([System.Array]$oneTraceUser)
        $Result = Compare-Users $NewADUser $NewTraceUser
        Write-Output $Result
    }
}

I now have a User class that, depending on the type of variable passed to it, will run one of two similar constructors that import data either from AD or a CSV file, and can go on to use this class to do comparisons between generic users.

Related