Почему неявное преобразование в [byte]
работает, а при замене byte
на bool
уже не работает?
т. е. следующие работы...
Add-Type -TypeDefinition @'
public readonly struct MyByte
{
private readonly byte value;
public MyByte( byte b ) => this.value = b;
public static implicit operator byte( MyByte b ) => b.value;
public static explicit operator MyByte( byte b ) => new MyByte( b );
public override string ToString() => $"{value}";
}
'@
[byte] $d = [MyByte]::new( 1 ) # OK
... в то время как этот очень похожий код не делает:
Add-Type -TypeDefinition @'
public readonly struct MyBool
{
private readonly bool value;
public MyBool( bool b ) => this.value = b;
public static implicit operator bool( MyBool b ) => b.value;
public static explicit operator MyBool( bool b ) => new MyBool( b );
public override string ToString() => $"{value}";
}
'@
[bool] $b = [MyBool]::new( $true ) # Error
Это приводит к следующей ошибке:
Cannot convert value "MyBool" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.
Обратите внимание, что в C# неявное преобразование в bool
работает как положено:
public class MyBoolTest {
public static void Test() {
bool b = new MyBool( true ); // OK
}
}
Так что это похоже только на PowerShell.
(PS-версия: 7.2.2)
@SantiagoSquarzon Разница в том, что первое является явным преобразованием. Оператор неявного преобразования можно использовать в обоих случаях, но PoSh, похоже, не распознает его для неявного случая.
@SantiagoSquarzon [bool][MyBool]::new( $false )
-> удивительно печатает True
!
@SantiagoSquarzon Как всегда, у @mklement0, похоже, ответ. Вкратце: преобразование в bool
полностью обрабатывается PowerShell и игнорирует любые неявные и явные операторы приведения (подробнее см. комментарии LanguagePrimitives.cs).
Кстати, $a = [MyBool]::new(1); $a | gm -force
теперь $a
больше не имеет значения LOL
Это нормально, поскольку вы применяете литая нотация. См. Ограниченные переменные и Типы переменных.
@JosefZ, ни одна из ваших ссылок не объясняет рассматриваемое поведение.
Вы уже сделали большую часть открытия самостоятельно с помощью Сантьяго Скуарзон, но позвольте мне попытаться подвести итог:
Вы видите два отдельных проблемных поведения PowerShell:
Проблемное поведение А: PowerShell имеет собственную встроенную логику преобразования в логическое значение, которая, к сожалению, нет учитывает неявные или явные операторы преобразования .NET.
Нижний раздел этот ответ обобщает правила этой встроенной логики, что объясняет, почему он рассматривает экземпляр Любые вашего [MyBool]
типа — даже [MyBool]::new($false)
— $true
, к сожалению.
Только в операциях, в которых экземпляр не приводится к логическому значению, учитываются операторы преобразования, что для большинства операторов означает использование экземпляра в ЛХС:
[MyBool]::new($false) -eq $false # -> $true
[MyBool]::new($false), 'other' -contains $false # -> $true
# With -in, it is the *RHS* that matters
$false -in [MyBool]::new($false), 'other' # -> $true
Напротив, если вы сила логический контекст — либо с помощью логического значения в (обычно) LHS, либо с приведением скрытый к логическому — встроенная логика PowerShell — которая не учитывает операторы преобразования — срабатывает:
$false -eq [MyBool]::new($false) # -> !! $false
$false, 'other' -contains [MyBool]::new($false) # -> !! $false
# With -in, it is the *RHS* that matters
[MyBool]::new($false) -in $false, 'other' # -> !! $false
# Most insidiously, with *implicit* coercion.
if ([MyBool]::new($false)) { 'what?' } # -> !! 'what?'
Проблемное поведение Б: Когда вы ограничение типа переменную с [bool]
, то есть когда вы помещаете литерал типа в слева присваиваемой переменной (например, [bool] $b = ...
, в отличие от $b = [bool] (...)
, [1] правила связывания [bool]
параметр - неожиданно и неуместно - срабатывают, которые, в отличие от встроенного преобразования любого типа в логическое, являются довольно ограничительными, как указывает сообщение об ошибке.
То есть только $true
, $false
и числа (с нулевым отображением на $false
и любым ненулевым значением на $true
) могут быть переданы параметру с типом [bool]
.
[bool]
встречаются редко, потому что логическая логика PowerShell идиоматически выражается с помощью параметра [switch]
, который, когда ему (необычно) задается явный аргумент, является еще более строгим и принимает только $true
и $false
.Это проблемное поведение — ненадлежащее применение логики параметр к (непараметрическому) переменные — является предметом Ошибка GitHub № 10426.
[1] The difference between the two is that type-constraining - [bool] $b = ...
- effectively locks in the data type of variable $b
, so that latter attempts to assign new values are coerced to the same type. By contrast, $b = [bool] (...)
merely applies an ad hoc cast to force a conversion, without preventing later assignments from assigning values with different data types.
Было бы неплохо понять, почему gm
также удаляет значение из экземпляра: D (мой последний комментарий к вопросу zett)
@ Сантьяго, такое поведение действительно очень любопытно. Обратите внимание, что удаляется не ценность, а то, что форматирование для отображения внезапно перестает работать (вместо значения печатает пустую строку). Вы увидите, что значение все еще там с "$a"
. Я рекомендую вам либо задать новый вопрос здесь, либо создать проблему на GitHub.
Как вы думаете, это ошибка?
@ Сантьяго: да, несомненно.
Хорошо, такое же поведение будет происходить и в классе PS со свойством hidden
. И это не только gm
нарушение форматирования для отображения, select *
тоже
Для интересующихся: Сантьяго опубликовал дополнительный вопрос здесь.
Отправил Ошибка GitHub № 17071, спасибо за исправление for-display formatting
:) Я связал ваш комментарий, чтобы этот вопрос и ответ был виден, если вы не возражаете, пожалуйста, не удаляйте комментарий
Странно, что
$b = [bool][MyBool]::new( $true )
работает, а[bool] $b = [MyBool]::new( $true )
нет