Reputation: 347
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
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