Search code examples
powershelloopoverridingbase-class

Powershell Call Base Class Function From Overidden Function


I want to make a call to the parent function from its overidden function, i isolated my problem in the following code:

class SomeClass{
  [type]GetType(){
    write-host 'hooked'
    return $BaseClass.GetType() # how do i call the BaseClass GetType function??
  }
}
SomeClass::new().GetType()

i am expecting an output like this:

hooked
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     SomeClass                                System.Object

Solution

  • In oder to call a method on the base class, cast $this to it (([object] $this).GetType(), in your case, given that your class implicitly derives from [object](System.Object)):

    class SomeClass  {
      [type]GetType(){
        Write-Verbose -Verbose hooked!
        # Casting to [object] calls the original .GetType() method.
        return ([object] $this).GetType()
      }
    }
    
    [SomeClass]::new().GetType()
    

    As an aside re referring to the base class in PowerShell custom classes:

    • PowerShell only allows you to reference the base class via the abstract base identifier in base-class constructor calls:

      class Foo { [int] $Num; Foo([int] $Num) { $this.Num = $Num } }
      class FooSub : Foo { FooSub() : base(42) { } } # Note the `: base(...)` part
      [FooSub]::new().Num # -> 42
      
    • In methods, the only (non-reflection-based) way to refer to the base class is via a type literal (cast), which in essence requires you to hard-code the base-class name (as also shown with ([object] $this) above):

      class Foo { [string] Method() { return 'hi' } }
      # Note the need to name the base class explicitly, as [Foo].
      class FooSub : Foo { [string] Method() { return ([Foo] $this).Method() + '!' } }
      [FooSub]::new().Method() # -> 'hi!'
      
      • Caveat: There appears to be a bug in Windows PowerShell (no longer present in PowerShell (Core) 7+), where calling a base class' method fails if the base class is a generic type that is constructed with type argument [pscustomobject] aka [psobject]. Use a reflection-based workaround in that case; e.g.:

        class Foo : System.Collections.ObjectModel.Collection[pscustomobject] {
          [void] Add([pscustomobject] $item) {
            Write-Verbose -Verbose hooked!
            # Use reflection to call the base class' .Add() method.
            # Note: In *PowerShell Core*, this workaround isn't necessary, and the usual would work:
            #         ([System.Collections.ObjectModel.Collection[pscustomobject]] $this).Add($item)
            # Note the use of .GetMethod() and .Invoke()
            [System.Collections.ObjectModel.Collection[pscustomobject]].GetMethod('Add').Invoke($this, [object[]] $item)
          }
        }