В чем смысл квалификаторов двойного вложенного типа в назначениях powershell?

#powershell

#powershell

Вопрос:

У меня есть следующий оператор powershell, который я пытаюсь понять, в частности, какой тип он делает своей переменной.

 [double] [boolean] $x = 12.5
$x
$x.GetType()
$x  = .5
  

Результаты 3-х использований $ x:

 1
Double
1
  

Итак, означает ли это, что $x всегда преобразует свои значения в логическое значение, но затем возвращает его как Double ?

Есть ли страница, которая документирует, что это должно делать?

Ответ №1:

Итак, означает ли это, что $x всегда преобразует свои значения в логическое значение, но затем возвращает его как Double

Вроде того, да. 12.5 (и любое последующее значение, присвоенное переменной) преобразуется в логическое значение $true , а затем $true преобразуется в числовое значение 1.0 .

Чтобы понять почему, я думаю, было бы полезно рассмотреть, как работает типизация переменных в PowerShell.

Из раздела about_Variables справки:

Типы переменных

В переменной можно хранить объекты любого типа, включая целые числа, строки, массивы и хэш-таблицы. И объекты, представляющие процессы, службы, журналы событий и компьютеры.

Переменные PowerShell свободно типизированы, что означает, что они не ограничены определенным типом объекта. Одна переменная может даже содержать коллекцию или массив объектов разных типов одновременно.

Тип данных переменной определяется .ЧИСТЫЕ типы значений переменной.

(курсив добавлен)

То есть:

  • Переменные в PowerShell НЕ вводятся интризически
  • Результирующий тип выражения переменной полностью зависит от сохраненного значения

Итак, что PowerShell предоставляет нам для управления типом значения, присвоенного переменной? Из того же раздела раздела справки выше:

Вы можете использовать атрибут типа и обозначение приведения, чтобы гарантировать, что переменная может содержать только определенные типы объектов или объекты, которые могут быть преобразованы в этот тип. Если вы пытаетесь присвоить значение другого типа, PowerShell пытается преобразовать значение в его тип. Если тип не может быть преобразован, оператор присваивания завершается ошибкой.

Чтобы использовать приведенную нотацию, введите имя типа, заключенное в квадратные скобки, перед именем переменной (в левой части инструкции присваивания). В следующем примере создается $number переменная, которая может содержать только целые числа, […]

 [int]$number = 8
$number = "12345"  # The string is converted to an integer.
$number = "Hello"
# ArgumentTransformationMetadataException thrown
  

Итак, PowerShell фактически не вводит саму переменную, но добавляет к ней что-то, что заставляет ее пытаться преобразовать тип при назначении — давайте посмотрим на это «что-то», что он добавляет.

Начните с присвоения переменной значения нужного типа, не пытаясь выполнить приведение с левой стороны:

 PS ~> $aBool = $true
PS ~> $aBool -is [bool]
True
PS ~> Get-Variable -Name aBool |Format-List

Name          : aBool
Description   :
Value         : True
Visibility    : Public
Module        :
ModuleName    :
Options       : None
Attributes    : {}
  

Обратите внимание, что (строковое представление) значения, True , выглядит правильно, но нет реального указания его типа — точно так, как мы ожидаем от действительно «свободно типизированной» переменной.

Теперь давайте сделаем то же самое, но с использованием приведенной выше нотации:

 PS ~> [bool]$aBool = 123 # Let's assign a non-[bool] value to a variable with a left-hand cast
PS ~> $aBool -is [bool]  # resulting value is still [bool], as expected
True
PS ~> Get-Variable -Name aBool |Format-List

Name          : aBool
Description   :
Value         : True
Visibility    : Public
Module        :
ModuleName    :
Options       : None
Attributes    : {System.Management.Automation.ArgumentTypeConverterAttribute}

PS ~> (Get-Variable -Name aBool).Attributes |Get-Member


   TypeName: System.Management.Automation.ArgumentTypeConverterAttribute

Name                            MemberType Definition
----                            ---------- ----------
Equals                          Method     bool Equals(System.Object obj)
GetHashCode                     Method     int GetHashCode()
GetType                         Method     type GetType()
IsDefaultAttribute              Method     bool IsDefaultAttribute()
Match                           Method     bool Match(System.Object obj)
ToString                        Method     string ToString()
Transform                       Method     System.Object Transform(System.Management.Automation.EngineIntrinsics engineIntrinsics, System.Object inputData)
TransformNullOptionalParameters Property   bool TransformNullOptionalParameters {get;}
TypeId                          Property   System.Object TypeId {get;}
  

Ага! У переменной по-прежнему нет типа, но теперь она содержит ArgumentTypeConverter атрибут.

Сам атрибут type converter также не является строго типизированным, но он всегда будет либо возвращать значение типа, указанного в приведении слева (или выдает ошибку преобразования):

 PS ~> $typeConverter = (Get-Variable aBool).Attributes[0]
PS ~> $typeConverter.Transform($ExecutionContext, 123)
True
PS ~> $typeConverter.Transform($ExecutionContext, '')
False
  

Поэтому, когда вы пишете оператор присваивания с несколькими приведениями типов в левой части выражения, PowerShell просто сохраняет весь список как отдельные атрибуты преобразователя типов:

 PS ~> [double][bool]$aDoubleBool = 123
PS ~> (Get-Variable aDoubleBool).Attributes

TransformNullOptionalParameters TypeId
------------------------------- ------
                           True System.Management.Automation.ArgumentTypeConverterAttribute
                           True System.Management.Automation.ArgumentTypeConverterAttribute
  

Мы не можем точно сказать по выводам, но первый — это преобразователь типов, сгенерированный из [bool] , последний — для [double] , то есть PowerShell связывает их, от самого правого до самого левого.

Мы можем повторить это поведение, вручную связав преобразования:

 PS ~> 
>> (Get-Variable aDoubleBool).Attributes |%{
>>   $value = $_.Transform($ExecutionContext, $value)
>> }

PS ~> $value -is [double]
True
PS ~> $value
1
  

Таким образом, вы можете добавлять и вычитать столько десятичных значений, сколько захотите, присвоенное значение всегда будет 0.0 или 1.0 из-за [bool] -> [double] преобразования 🙂