Ginger_Chacha
Ginger_Chacha

Reputation: 347

Powershell: How to call a static method from the other static method

I have a class of "utils" and some of the static methods. I want to call one method from the other. If I use $this it will tell me "Cannot access the non-static member 'this' in a static method or initializer of a static property." but without $this it can't find the method. I also don't want to hard code the class name - but if this is the only solution then I have to. Can you please shed some lights here? Environment: Win10/Powershell 7.1 Below please find the code

class MyDateUtils
{
    
    Static [DateTime] ToLocalTime([String]$fromDate,[String]$fromTZ, [String]$format)
    {
    
        if($fromTZ -eq $null -or $fromTZ -eq "")
        {
            $fromTZ = 'Eastern Standard Time'
        }
        
        $tz = [TimeZoneInfo]::FindSystemTimeZoneById($fromTZ)

        $nominalDate = FormatDate($fromDate, $format)

        $utcOffset = $tz.GetUtcOffset($nominalDate)

        $dto = [DateTimeOffset]::new($nominalDate.Ticks, $utcOffset)

        return $dto.LocalDateTime

       
    }
    
    Static [DateTime] FormatDate([String]$date)
    {
        return FormatDate($date, $null)
    }

    Static [DateTime] FormatDate([String]$date,[String]$format)
    {
    
        #$dateString = $date.split(' ')[0]
    
        if($format -ne $null -and $format -ne "")
        {
            return [Datetime]::ParseExact( $date, $format, $null)
        }
    
    
        $formatList =  'MM/dd/yyyy', 'MM/dd/yyyy HH:mm:ss', 'MM/dd/yyyy HH:mm',`
                       'M/d/yyyy', 'M/d/yyyy HH:mm:ss', 'M/d/yyyy HH:mm',  
                       'MM/d/yyyy', 'MM/d/yyyy HH:mm:ss', 'MM/d/yyyy HH:mm',
                       'M/dd/yyyy', 'M/dd/yyyy HH:mm:ss',    'M/dd/yyyy HH:mm',
                       'yyyy-MM-dd', 'yyyy-MM-dd HH:mm:ss', 'yyyy-MM-dd HH:mm'
    
    
        $result = $null
    
        foreach($f in $formatList)
        {
          try{
            $result = [Datetime]::ParseExact( $date, $f, $null)
          }catch {
            
          }
          
          if($result -ne $null)
          {
            return $result
          }
        }
    
        return $result
    }
    
    


} 

Then if I ran below from command line I got the error below:

[MyDateUtils]::ToLocalTime('2021-02-23 07:10', $null, $null)
line |
  14 |          $nominalDate = FormatDate($fromDate, $format)
     |                         ~~~~~~~~~~
     | The term 'FormatDate' is not recognized as a name of a cmdlet, function, script file, or executable
     | program. Check the spelling of the name, or if a path was included, verify that the path is correct
     | and try again.

if I added a $this prior to the FormatDate it then populated the static/this error Appreciate for your help

Upvotes: 2

Views: 1282

Answers (1)

mklement0
mklement0

Reputation: 438763

I don't want to hard code the class name - but if this is the only solution then I have to.

Indeed, unlike in C#, for example, you have to use the class name explicitly to refer to a static member of your class even from inside that class:

Using simplified examples:

The (near)-equivalent of the following C# class:

// C#
public class Foo {
  public static string Bar() {
    // Calling fellow static function Baz() requires NO qualifier.
    return Baz();
  }
  static string Baz() {
    return "baz C#";
  }
}

in PowerShell is the following:

# PowerShell
class Foo {
  static [string] Bar() {
    # To call the fellow static Baz() method,
    # you MUST use the class name explicitly.
    # Otherwise, due to PowerShell's *dynamic scoping*, PowerShell would 
    # look for a command named 'Baz' in the enclosing scope and its ancestors.
    return [Foo]::Baz()
  }
  hidden static [string] Baz() {
    return 'baz'
  }
}

Note: Baz() without qualifier and without arguments would actually be syntax error, but with one ore more arguments PowerShell indeed would look for a command named Baz at runtime - even though using method syntax (argument list enclosed in (...) and arguments separated by ,) is inappropriate for commands - see this answer for more information.

PowerShell always requires an explicit qualifier for class-internal member access:

  • $this is required for instance members; e.g., $this.PropA is needed to access an instance property declared as [string] $PropA, and $this.OtherMethod() calls fellow instance method OtherMethod.

  • For static members, an object representing the class itself, which is a type literal in the simplest case, such as [Foo], and notably also :: rather than ., as shown above.

See the conceptual about_Classes help topic for more information.

While the automatic (implicitly declared) $this variable is a convenient abstraction to refer to a class' instance, no analogous variable exists for the class itself, invariably resulting in needing to repeat the class name inside the class, which is both somewhat cumbersome and a maintenance concern.

If - and only if - you're referencing a static member from an instance member, you can work around the need to repeat the class name by using $this.GetType():

class Foo {
  # Bar() is now an *instance* method.
  [string] Bar() {
    # Because $this is now defined to refer to the instance,
    # you can use $this.GetType() to refer to the class.
    return $this.GetType()::Baz()
  }
  hidden static [string] Baz() {
    return 'baz'
  }
}

[Foo]::new().Bar() # -> 'baz'

Certainly, having an automatic variable analogous to $this would help matters, such as $thisType ($thisClass would be semantically too narrow if interface definitions were to be supported in the future).

If you feel strongly enough about having such a variable, I encourage you to create a feature request in PowerShell's GitHub repo.

Note:

  • One general concern when introducing new automatic variables are potential name collisions, given PowerShell's dynamic scoping.

  • Classes were introduced late in the evolution of PowerShell, and while PowerShell fully supports interacting with preexisting .NET classes (types), defining them in PowerShell itself is a less pressing need, especially for casual use (where ad hoc objects created with [pscustomobject] @{ ... } will often do). While PowerShell's class support will never catch up to C#'s, a variety of improvements are being considered - see GitHub issue #6652.

Upvotes: 4

Related Questions