Monday, July 5, 2021

PowerShell: Safely Converting Strings to by Value Types

Converting strings to value types like int, char, double, bool, DateTime, TimeSpan, and Guid without throwing an exception when invalid data is encountered requires some finesse.  One approach would be to return a $null of the conversion from string to value type fails. In a previous post, PowerShell: Nullable Types, nullable PowerShell types were introduced which facilitates returning a $null when a conversion from string to value type fails. Also in a previous post, the ref keyword was introduced (PowerShell: Passing Values by Reference (the ref Keyword)) which allowed PowerShell to take advantage of .NET's TryParse methods.

The PowerShell class below, Converts, demonstrates how to covert a string to type int, double, bool, and DateTime without generating an error/exception (using nullable types and the ref keyword):

class Convert {
  static [Nullable[DateTime]] GetDateTime([string] $candidate) {
    # Trying to pass in $result=$null causes TryParse to fail if 
    # $candidate is invalid
    Nullable[DateTime]] $result = Get-Date
    [bool] $valid = [DateTime]::TryParse( 
                            $candidate, 
                            [ref]$result)

    if (!($valid)) {
      $result = $null
    }

      return $result                
  }

  static [Nullable[int]] GetInt([string] $candidate) {
    [Nullable[int]] $result = $null
    [bool] $valid = [int]::TryParse( 
              $candidate, 
              [ref]$result)

    if (!($valid)) {
      $result = $null
    }

    return $result        
  }
  
  static [Nullable[bool]] GetBool([string] $candidate) {
    [Nullable[bool]] $result = $null
    [bool] $valid = [bool]::TryParse( 
              $candidate, 
              [ref]$result)

    if (!($valid)) {
      $result = $null
    }

    return $result        
  }
  
  static [Nullable[double]] GetDouble([string] $candidate) {
    [Nullable[double]] $result = $null
    [bool] $valid = [double]::TryParse( 
              $candidate, 
              [ref]$result)

    if (!($valid)) {
      $result = $null
    }

    return $result        
  }  
}

Code to test the above class is as follows:

[string] $badCandidate = 'abc'
[string] $goodCandidate = '12/25/2021 08:09:11'
[Nullable[DateTime]] $dateTimeResult = [Covert]::GetDateTime($badCandidate)

if ($null -ne $dateTimeResult) {
  throw "$badCandidate should result in an invalid DateTime"
}

$dateTimeResult = [Covert]::GetDateTime($null)

if ($null -ne $dateTimeResult) {
  throw "`$null should result in an invalid DateTime"
}

$dateTimeResult = [Covert]::GetDateTime($goodCandidate)
if ($null -eq $dateTimeResult) {
  throw "$goodCandidate should result in a valid DateTime"
}

$goodCandidate = '123'
[Nullable[int]] $intResult = [Covert]::GetInt($badCandidate)

if ($null -ne $intResult) {
  throw "$badCandidate should result in an invalid int"
}

$intResult = [Covert]::GetInt($null)

if ($null -ne $intResult) {
  throw "`$null should result in an invalid int"
}

$intResult = [Covert]::GetInt($goodCandidate)
if ($null -eq $intResult) {
  throw "$goodCandidate should result in a valid int"
}

$goodCandidate = 'False'
[Nullable[bool]] $boolResult = [Covert]::GetBool($badCandidate)

if ($null -ne $boolResult) {
  throw "$badCandidate should result in an invalid bool"
}

$boolResult = [Covert]::GetBool($null)

if ($null -ne $boolResult) {
  throw "`$null should result in an invalid bool"
}

$boolResult = [Covert]::GetBool($goodCandidate)
if ($null -eq $boolResult) {
  throw "$goodCandidate should result in a valid bool"
}

$goodCandidate = '123.123'
[Nullable[double]] $doubleResult = [Covert]::GetDouble($badCandidate)

if ($null -ne $doubleResult) {
  throw "$badCandidate should result in an invalid double"
}

$doubleResult = [Covert]::GetDouble($null)

if ($null -ne $doubleResult) {
  throw "`$null should result in an invalid double"
}

$doubleResult = [Covert]::GetDouble($goodCandidate)
if ($null -eq $doubleResult) {
  throw "$goodCandidate should result in a valid double"
}

No comments :

Post a Comment