Powershell: 建议:实现空合并,空条件访问(空浸泡),空条件赋值

创建于 2017-03-02  ·  71评论  ·  资料来源: PowerShell/PowerShell

空合并空条件访问(null-soaking)将是对该语言的方便补充。

_Update_: @BrucePay还建议使用空条件_assignment_ ,并带有?= -参见下面的评论

例如,代替编写:

if ($null -ne $varThatMayBeNull) { $varThatMayBeNull } else { $fallbackValue }
#
if ($null -ne $varThatMayBeNull) { $varThatMayBeNull.Name } else { $null }

一个人可能会写:

$varThatMayBeNull ?? $fallbackValue  # null-coalescing
# 
$varThatMayBeNull?.Name   # null-conditional access, for Set-StrictMode -Version 2+
$varThatMayBeNull?[42]  # ditto, with indexing

关于空条件访问:将Set-StrictMode为OFF(默认值),您可以只使用$varThatMayBeNull.Name -不需要特殊的语法; 但是,如果Set-StrictMode -Version 2或更高版本有效,则$varThatMayBeNull.Name会_break_,这就是空条件运算符( ?. )有用的地方,它表明了明确的意图来忽略$null简明扼要。


公开问题

$varThatMayBeNull?[42]处理变量为$null ,但如果不是,则具有指定索引_must存在_的数组元素。

因此,使_indexing_为空条件也将很有帮助-_not_ (必须使用.ElementAtOrDefault() )。

两个基本选择是:

  • 提出其他语法,这些语法明确表示要忽略不存在的_index_的意图:

    • 问题是,鉴于?[...][...]?由于歧义而不可选项,为此选择哪种语法。
    • 也许[...?] ,但这似乎很尴尬。
  • 取决于Set-StrictMode设置所隐含的与访问不存在的索引有关的现有行为-参见下表


相关:实现三元条件

Issue-Enhancement Resolution-Fixed WG-Language

最有用的评论

@rjmholt在这里进行了分析。 快速分解:在将近40万个脚本中,变量名超过22,000,000(不是唯一),只有〜70个以?结尾的唯一变量。

所有71条评论

@ mklement0似乎第二个示例按设计没有“?”。
也许更改为name()

@iSazonov :好点,但是它只有在没有?情况下才有效,除非Set-StrictMode -Version 2或更高有效-我更新了最初的帖子以使之清楚。

我认为语法糖是powershell可以使用的,其他语言也可以使用。 对该提案+1。

考虑以下其他语言的示例:

a="${b:-$c}"
a = b || c;
a = b or c
a := b ? c
a = b ? b : c;

甚至

a = b if b else c

胜过

if (b) { a = b } else { a = c }

通常,您需要用额外的详细说明

a =(如果(b -ne $ null){b}否则{c})

使人感到肮脏和肮脏。

发现自己今天正在做此事。

Powershell
$ word =($ null,“两个”,“三个”)。其中({$ _ -ne $ null},“ First”)
``

我使用类似的模式:

$word = ($null, "two", "three" -ne $null)[0]

@kfsone您的示例$a=(if ($b -ne $null) { $b } else { $c })有一个小错误。 它应该是$a=$(if ($b -ne $null) { $b } else { $c })但是唯一需要$( )的PowerShell版本是版本1。从v2开始,您可以简单地执行以下操作:

$a = if ($b) { $b } else { $c }

@bgshacklett你不是说$word=($null,'two')[$null -ne 'three']吗?

不幸的是,它已经从6.1变为6.2到“未来”。

@ TheIncorrigible1不,如果您复制并粘贴上面添加的内容,则应该看到$word值设置为“两个”。 在我第一次看到该模式的“堆栈溢出”答案中有更多详细信息:
https://stackoverflow.com/a/17647824/180813

尽管20年前我曾进行过怪诞的黑客活动,但我写了一篇shebang来注册和/或查询RIPE-DB用户或组织记录,但我希望从powershell那里得到的是鼓励我的同事使用该语言的东西而不是向他们灌输恐惧。

我的试金石测试是这样的:我是否想在元旦凌晨3点通过平板电脑阅读该报告,而电话上的CEO却对我哭泣,这使我们损失了一秒钟几百万美元。

(另外:这只是对实际经历的一个夸大的夸张,直到我在Facebook工作并从一次紧急的快速泄密回来后才被告知,在我离开的两分钟内,空无一人的人超过了荷兰的人口新闻提要。这不是我的代码,而错误是在非常特定的模板情况下,c ++标准中return x vs return(x)的语义发生了微小的变化,“您想在2分钟内阅读这段代码吗?截止日期,一个饱满的膀胱,以及每一个将荷兰人的猫照片戴在肩膀上的木log的命运???

对。 PS中可能存在一些巧妙的技巧,这些技巧在紧要关头或需要一次性简写的情况下非常有用。

对于可维护的代码,理想情况下,我们应该使用像C#这样的显式null运算符。 对我而言,主要的麻烦是-我们将这些用于什么? ?已经被别名为Where-Object (我很想删除它,这是非常普遍的用法)。 请注意, %ForEach-Object别名,但这并不妨碍模运算,因此理论上至少让?可以是空值运算符精细; 它只会在表达式中这样解释,其中Where-Object并不是真正有效的。

@ vexx32

至少_syntactically_ ??应该没问题,因为我们正在谈论_expression_模式,而?作为命令[alias],只能在_argument_模式下识别。
不知道符号的重用是否会引起混乱,但这并不是符号在不同上下文中首次承担双重职责。

出人意料的是,使用?.?[]进行空值浸泡在技术上是一项重大突破,因为PowerShell当前允许?作为变量名中的非初始字符。

PS> $foo? = @{ bar = 1 }; $foo?.bar   # !! $foo? is a legal variable name
1

但是,我希望这将被视为存储区3:不太可能出现灰色区域更改。

我不能说我见过的任何人使用? 以变​​量名...我也不会,因为可能会被误读。 但是,是的,希望它应该完全可用。

我怀疑提出这个建议还为时已晚,但是Bash通过其参数替换功能( https://www.tldp.org/LDP/abs/html/parameter-substitution.html )处理了此问题(以及其他一些情况)

尽管由于可以完成的事情数量众多而使学习困难,但它的功能却异常强大。 我知道,由于PowerShell使用带括号的变量的方式将不可能使用这种确切的表示法,也不一定符合该语言的一般感觉,但这似乎是有用的数据点。

@bgshacklett

是的,参数替换功能强大,但是不幸的是,它不仅是_learn_的负担,也是_remember_的负担。

因此,尽管Bash的_features_经常很有趣,但它们的_syntactic form_却常常是不可思议的,难以记住的,并且不太适合PowerShell。

_Brace expansion_(如a{1,2,3}bash扩展到a1 a2 a3 )是一个有趣的功能,其表现我很乐意在PowerShell中看到的例子,但PowerShell的合适语法-参见#4286

我完全同意。 我提出的更多内容是作为在其他地方解决此问题的示例,而不是确切的解决方案。

还有另一位运营商可能要考虑:

$x ?= 12

如果未设置(不存在),则会设置$x 。 这是“初始化器模式”的一部分,“初始化器模式”在常规语言中并不常见,但对于(动态范围化的)语言(如外壳,make工具等)来说却很常见。如果用户未指定默认值,则可以使用脚本设置默认值。 (尽管实际上参数初始化程序非常普遍。)

扩展到属性:

$obj.SomeProperty ?= 13

如果对象不存在,则会在该对象上添加并初始化一个便笺属性SomeProperty

并且-为了好玩-使用-or初始化变量的另一种变化:

$x -or ($x = 3.14) > $null

$x ?= 12听起来是个好主意。

我想到我们可能不仅应该在LHS不存在的情况下,而且应该在确实存在但恰好包含了$null情况下应用所有这些运算符(将[System.Management.Automation.Internal.AutomationNull]::Value$null )。

添加并初始化note属性SomeProperty

因此, $obj.SomeProperty ?= 13对我来说才有意义,只有.SomeProperty存在并且包含$null ,因为即使使用常规分配也无法隐式创建属性(相反,对于_hasthables_隐式条目创建是有意义的)。

讨论的所有运营商都将需要从严格模式存在检查中豁免LHS。

我想知道为什么strictmode阻止您访问不存在的属性的意图/理由。 是否打算捕捉像$x.pretnd这样的错字? 是否打算检查诸如assert(x != null)类的假设? 是否打算阻止在代码中进一步传播$null

null浸泡(?。)很有帮助,以简洁的方式表明要忽略$ null的明确意图。

那不是.已经在非严格模式下发出的信号吗? 如果您想严格一点,可以先检查.Name存在,如果不先检查,则表明您已经明确表示检查对您来说并不重要。

如果StrictMode的目的是禁止访问不存在的属性并禁止返回$null ,则将不同的推理应用于?. ,并且出于相同的原因应该抛出相同的异常。 如果没有,为什么不呢?

使用空合并,您可以编写:

$result = Invoke-RestMethod -Uri 'https://example.org/api/test'
$test = $result?.Name

在每个属性引用中总是使用?.会很快成为推荐或惯用的做法,因为它“就像.那样,并且不会为使用strictmode的人抛出异常”?

真正希望拥有带有可变设置的可配置strictmode,以便例如可以在脚本# StrictMode SkipMemberExistenceCheck的顶部具有一个声明,或用于指示unstrict code块的转义填充属性通常? [我怀疑这种愿望是简洁的C#样式语法,但是您知道我的意思]

感谢您的想法,@ HumanEquivalentUnit。

是否打算捕捉像$x.pretnd这样的错字?

我不能说出设计意图,但这对我来说很有意义-类似于Set-StrictMode -Version 1对_variables_(仅)所做的事情。

对于变量,是关于它们的_existence_,而不是值是否恰好是$null ,并且检查属性是否存在遵循相同的模式。

我认为动态脚本语言可以找到最接近静态类型的编译语言将在_compile time_报告的错误。


那不是什么。 信号已经处于非严格模式? 如果您想严格一点,可以先检查.Name是否存在

是的,但是在非严格模式下,您会放弃上述存在性检查,并且手动测试显然非常麻烦-并且总是比内置功能慢。

通过不首先进行检查,您已经表明了明确的意图,即检查对您而言并不重要。

关键是,使用Set-StrictMode -Version 2或更高,您将获得:

  • 成员存在检查的好处_默认情况下_
  • .?简洁地_按需退出的选项

您可以在脚本# StrictMode SkipMemberExistenceCheck的顶部有一个声明,或通常用于指示unstrict code块的转义填充属性

相关:当前Set-StrictMode动态范围界定存在问题; 请参阅@lzybkr的RFC草案,以实现_lexical_严格模式: https :

默认情况下成员存在检查的好处

PS包含执行成员检查的代码,如果不存在则返回$ null。 默认情况下,这是一种好处,如果您愿意,也可以通过使用$x.psobject.properties...编写自己的成员存在性测试来避免这种情况。 StrictMode消除了这种好处,并消除了必须编写存在性测试的障碍。 然后,而不是提供一种精确地编写成员存在性测试的好方法, ?.将为您提供一种简洁的解决方法,以获取.始终执行的操作。 而且,如果该成员存在很重要,您仍然必须单独编码。

但这确实使我问起有关方法调用的问题:

$x.pretend     -> $null
$x.pretend()   -> Exception MethodNotFound
$x?.pretend  -> $null
$x?.pretend()   -> ____ what goes here? $null?

set-strictmode -version 2
$x.pretend -> Exception PropertyNotFoundStrict
$x.pretend()  -> Exception MethodNotFound
$x?.pretend  -> $null
$x?.pretend()   -> ____  ?

例如,对于非严格模式下的属性查找, ?.行为与.相同,但对于非严格模式下的方法调用,其行为与.有所不同,从而使其变得细微而不即插即用的替代品。 这是否意味着在使用?.您根本无法判断是否已调用方法,或者什么也没有发生? 属性可以由getter方法来支持,但是我认为DotNet中的约定是getter不能改变内部状态,而是正常的方法调用来改变状态。 使用?.您是否不知道是否要更改对象的状态?

我想知道是否可以通过使??能够直接从左侧的表达式处理PropertyNotFoundException和MethodNotFound Exceptions(但不能从方法内部引发的其他Exceptions)以及处理$获得更好的好处?为null,并且根本没有?. 。 例如

$varThatMayBeNull ?? $fallbackValue # null-coalescing
#
$varThatMayBeNull.Name ?? $fallbackvalue    # catching PropertyNotFoundStrict exception
$varThatMayBeNull.Name ??  # default fallback is $null
$varThatMayBeNull.Method() ??  # catching MethodNotFound Exception

# and potentially addressing the index case too
$varThatMayBeNull.first.second[3].Method() ??  # catching MethodNotFound Exception, or index not found Exception

然后,与其问?.而不是调用成员或访问属性,它可能是询问成员是否存在并返回[bool]的一种简洁方法。

使用???.相同的语法添加项,您可以处理更多的情况,并且减少了混乱的重叠。

StrictMode消除了这种好处,并消除了必须编写存在性测试的障碍

一个人的障碍就是另一个人的利益:
这完全取决于您想发生的情况(默认情况下_):

  • 如果您确信代码中没有_typos_-一定要利用_default_行为(严格模式为OFF)-并且您将不需要.? (用于_property_访问-请参见下一条评论有关方法调用和索引编制)。

    • 在_personal_注释上:我倾向于使用Set-StrictMode -Version 1来避免_variable_名称中的错别字,但我发现-Version 2或更高版本太烦人了,主要是由于#2798(这是一个与该提案)。
  • 如果要确保您(a)不会拼写错误的属性名称和/或(b)您的代码不会意外地对非您打算使用的数据类型进行操作,请将严格模式设置为版本2或更高版本。

    • 确实,_当前地_使得按需选择_退出这些检查变得很麻烦,
    • 这就是为什么在此处提出引入空条件访问的原因:使其易于按需退出。

明确测试成员的存在确实是一个单独的用例,无论使用哪种严格模式,这都是必要的。

至于_方法调用_,OP并未涵盖:

_方法调用_(例如$varThatMayBeNull.Method() )和_indexing_(例如$varThatMayBeNull[$index] )是两种情况,即使关闭严格模式或使用版本1也会受益,因为这两种情况当前都不可避免地导致错误:被访问的值为$null

语法与方法的属性- $varThatMayBeNull?.Method() -相同,并且与索引类似- $varThatMayBeNull?[$index] -与C#中的语法相同。

_Update_:索引还有第二个方面,即变量是否为非null,但给定索引处的元素不存在(例如$arr = 0,1; $arr[2] )。 在严格模式版本2或更低版本中,其计算结果为$null ,但是在版本3或更高版本中,它将导致错误,并且能够退出该错误也将是一件很不错的事情。 但是,这种情况的语法带来了挑战-请参阅更新的OP,该OP当前提出了可能比较尴尬的[...?]

而且,是的,即使对于方法,我也将这种访问默认设置为$null -再次,您是_explicitly_表示如果要访问的对象是$null则不执行任何操作是可以的如果不存在这样的数组元素。

请注意,从第二个意义上讲,C#没有空条件索引。

它具有空条件索引,只是不太像您的建议。 C#允许(我至少可以肯定地……) array?[0]绕过通常的“索引为null数组/集合”的问题,并仅返回null。

但是,是的,我同意这是一个好主意。 它使我们免于进行异常处理,并使代码保持清晰,简洁。

@ vexx32,那就是空条件索引_one_方面:如果arrayarray?[0]null时,索引操作将被忽略- C#有过,因为你的状态。

我在(更新:_also_)谈论空条件索引的_other_方面: array中的array?[0]是_not_ null但它是_ 0元素

据我所知,在C#中不能忽略后者,但是我认为它也很有用。

您能想到用术语来赋予这两个方面不同的名称吗?

另一个JavaScript问题的代码将从$_.Description[0].Text变为$_?.Description[0?]?.Text ,我认为这并不好。 我对??行为提出的另一条建议将其带到$_.Description[0].Text ?? ,这在主观上会更好,并且(如果可能实现)可以一次性处理链中任何查找点的故障,并且仍然可以让您选择默认情况下要发生的事情,并且可以随意进行其他后备,而不仅限于$null

顺便说一句,此?.语法是否具有静态属性和方法的类似物,即$t = [int]; $t::MaxValu; $t::Pars("1") ,会变成?:?::吗?

指定多个索引的数组索引的预期行为是什么? $array[0,4?,1,2]可以允许每个人失败,还是$array[0,4,1,2?]仅允许整个批次成功或失败?

首先,我必须退后一步,因为我意识到我已经将值得分开的各个方面(至少在概念上)混为一谈:

  • _Null-value_-conditional成员或索引访问,如C#中所示:

    • 如果正在访问的值是$null ,则忽略尝试访问其成员或对其进行索引并评估为$null尝试。
  • _Member-existence_-conditional成员或索引访问权限,特定于PowerShell:

    • 如果正在访问的值是_non- $null _,请忽略尝试访问不存在的_members_(属性,方法调用,索引)的尝试。

注意:为简便起见,在这里我稍微宽松地使用_member_来包含碰巧是集合的对象的_elements_,这些对象可以通过_indexing_而不是点表示法进行访问。

如果您确信代码中没有拼写错误-一定要利用默认行为(严格模式为OFF)-您将不需要。? (用于物业访问

@ mklement0我认为您是在从“一个人使用严格模式来改进自己的代码”的角度讨论的,而我从“其他人可以在严格模式下运行我的代码”的角度讨论,那么哪些更改也会使我的代码对他们也有效? ”,这使我无法接受不同的推理。 使用?. ,它比.更加方便使用,并且使代码也可以在严格模式下工作,以至于我也可以习惯性地在任何地方使用它。 我一无所获,并获得了兼容性。 我担心所有PowerShell代码都会以这种方式丑化,因为在更多情况下它可以“​​工作”。

但这不是直接替代,因为它在非严格模式下使失败的方法调用查找静音,而在严格模式下使失败的属性查找静音-它为成员查找增加了一点细微差别,这是一种非常常见的操作。 它并不能使想要简化模板的人从严格模式检查中受益。 它增加了复杂性,因此在足够的情况下并不能帮助足够的人。 而且,使代码更加兼容也太方便了,我认为“如果您不想要它,请忽略它”不足以阻止它。

PowerShell可以执行C#不能执行的操作,例如设置自动变量,或者具有在后台抛出异常并使其安静的先例。 必须有一种更好的方法,它根本不使成员查找复杂化,但是简化了严格模式安全检查样板,以便希望使用该检查的人生活更轻松,而那些因检查而烦恼的人的生活也更轻松。

@HumanEquivalentUnit如何使这些内容静音? 您必须事先编写大量的异常处理和空检查,而不是:

$prop = $item?.Method().PropIwant ?? 'PropActionFailed'

v。

$prop = if ($null -ne $item) {
    $propIwant= $item.Method().PropIwant
    if ($null -eq $propIwant) {
        'PropActionFailed'
    }
    else {
        $propIwant
    }
}

这种情况并不少见。 或者,您只是抛出$ErrorActionPreference = 'Stop'并将整个内容包装在try/catch以简化null检查,这是一个错误的模式(异常驱动逻辑)。

同样,这是语言的未来,这不像我们要回到5.1并进行更改一样。 如果您想要更多的采用者,则需要使阅读代码更容易,我认为此建议确实如此。

@ mklement0我认为您是在从“某人使用严格模式来改进自己的代码_”的角度进行讨论,而我从“其他人可以在严格模式下运行我的代码的角度进行讨论,哪些更改也会使我的代码对他们也有效? _”

我们是在谈论“ set -euo pipefail”(严格限制模式)等效方式吗?

a-变量查找的限制(尽管我不明白为什么您认为对成员查找进行简单的null检查是一件坏事,但实际的成员查找要比if (result is null)贵得多)
b-在作用域/代码块上使用[Strict]告诉解析器将它们视为此类,
c-严格性基于范围:未标记为严格的代码并不严格。

如果我编写的严格代码在使用时失败,那么我们中的一个人就犯了一个错误。 我在从未测试过的代码中引用了丢失的变量,或者您误解了我设计不良的API,这取决于未传递给我的函数的变量的存在,而您未能填充我希望填充的变量。 。

# Their code
Function Log($Message) { Write-Host $Massege }  # not strict

# My code
[Strict]
Function DebugMsg
{
  Param([String] $Message)
  if ($DEBUG) { Log Host $Message }
}

# Your code
DebugMsg "Hello"  # you didn't define DEBUG, and by saying strict I expressed a contract whereby the variable MUST be defined.

未将“日志”功能标记为严格,因此不会抱怨拼写错误

mention现在您提到了它, [Strict()]属性将是一种以范围限制的方式实现严格模式的_awesome_方式。 在严格模式问题的其他问题线程中需要提到这一点,我将对此进行挖掘并链接回这里...

@ TheIncorrigible1如何使这些内容静音? ”在非严格模式下, $null.x是静音的, $null.x()是一个例外。 在讨论了?.$null?.x是静默的, $null?.x()是静默的。

所以,你就会有一个.其工作方式的C# ?. ,除了严格模式它抛出异常。 然后,您将获得一个?. ,它可以在严格模式下工作,例如C#的?.而PowerShell的.可以在非严格模式下工作,但是与PowerShell的.非严格模式下的?.的组合在其中更多情况下显然更好,更有用)。

您将不得不事先编写大量的异常处理和空检查,而不是: $prop = $item?.Method().PropIwant ?? 'PropActionFailed'

您的方法调用可能会抛出,或者返回$ null,而PropIWant可能会丢失,您仍然必须编写:

$prop = try {
    $item?.Method()?.PropIwant ?? 'PropActionFailed'  
} catch [someMethodException] {
    'PropActionFailed'
}

与我建议的?? (如果可以构建)相对,并且不需要?. ,它涵盖了方法异常的可能情况,以防万一您关心的只是“成功与否”并且没有详细的错误处理。

$prop = $item.Method().PropIWant ?? 'AnythingFailed'

如果您想要更多的采用者,则需要使阅读代码更容易,我认为此建议确实如此。

您多久在PowerShell中键入. ? 您想多久向新用户解释一次.?.的区别,并告诉他们每次输入.都必须考虑一下区别?

“哦,那是怎么发生的?” “在C#中, .引发了缺少成员的异常,因此他们发明了?. ,但事实并非如此。这很棒,因此默认情况下,他们在PowerShell中使用了.不会抛出于是他们发明了严格的模式,这使得。 .的行为很像C#和扔但只有在这种情况下,但一直到手动检查成员真的很罗嗦和麻烦,没人愿意键入。他们没有直面解决该问题并使其更方便地从strictmode中受益,而是躲开了这一点,并模仿了C#的?.而不是您所要求的严格性之外的偷偷摸摸的方法,这有点方便对您来说,但对其他所有人来说都是糟透了。您仍然没有成员存在测试,并且从strictmode中受益仍然是冗长而烦恼的,但是至少您不必受此困扰,因为“ strictmode”具有“ normalmode”逃生舱口现在”。 ck

如果我编写的严格代码在使用时失败,那么我们中的一个人就犯了一个错误。

尽管我不明白为什么您会认为对成员查找进行简单的null检查是一件坏事,但实际的成员查找要比if(结果为null)贵得多

我认为这不会很糟糕,PS非严格模式已经可以做到这一点,并且我认为这将是提议的?.的理想选择-非常不同,在更多情况下有用。

但是请注意,在PS中您可以执行C#无法执行的操作:

(1..3)[1kb..100kb]

在非严格模式下也不例外。 更改数字,看看需要多长时间; 从性能以及我认为它必须在幕后才能工作以索引到任意对象的角度来看,为了方便起见,似乎实际上有大约100,000个异常被提出和消除。 PS并不总是做出“必须尽可能快,给用户带来不便”的选择,我对此很喜欢。

@HumanEquivalentUnit在您的职业生涯中可能从未使用过的每种语言都有很多功能,如果您不喜欢该样式,那很好,您不需要使用这些功能。 我不明白你为什么要取消严格模式。 在脚本中这是一个好习惯,因此您要有意识地处理问题,而不是让语言吞并问题(本质上是到处都有隐式的空catch块)。 同样,仍然欢迎您不要使用这些功能(例如,我从未使用过Out-Default )。

另外,运算符是? ,而不是?. ; 它也可以与索引访问一起使用。

为了继续为进一步的讨论打下基础,让我总结一下现有的严格模式如何影响空值条件访问和成员存在条件访问上面介绍的术语):

下面的列代表Set-StrictMode设置,并且列值指示:

  • allowed ...允许
  • prohibited ...禁止(导致语句终止错误)

顺便说一句:错误仅是_statement-_而不是_script_-terminate,这一事实与有关PowerShell错误处理的更大讨论有关-请参阅https://github.com/PowerShell/PowerShell-Docs/issues/1583。

  • _Null-value_-conditional access - $obj _itself_是$null

构造| -关| -版本1 | 版本2 | -版本3+
---------- | ---- | ---------- | ---------- | ------------
$ obj | 👍| 🚫| 🚫| 🚫
$ obj.Prop | 👍| 👍| 🚫| 🚫
$ obj [42] | 🚫| 🚫| 🚫| 🚫
$ obj.Method()| 🚫| 🚫| 🚫| 🚫

奇怪的是,对于-Off-Version 1 ,允许$obj.Prop ,而不允许$obj[42]

  • _Member-existence_-conditional access - $obj不是- $null ,但是所访问的成员/元素不存在:

构造| -关| -版本1 | 版本2 | -版本3+
---------- | ---- | ---------- | ---------- | ------------
$ obj.Prop | 👍| 👍| 🚫| 🚫
$ obj [42](可索引)| 👍| 👍| 👍| 🚫
$ obj [42](不可索引)| 👍| 👍| 🚫| 🚫
$ obj.Method()| 🚫| 🚫| 🚫| 🚫

很好奇的是,即使使用-Version 2 ,固有可索引的对象(例如,数组)也允许访问不存在的元素,而标量(PowerShell提供索引功能是为了与集合进行统一处理)不允许这样做。

在最新的StrictMode中运行并尝试访问可能存在或不存在的属性时,几乎必须使用null浸泡(在C#中

function Get-OptionalPropertyValue($object, [string] $propertyName) {
    if (-not ([bool] (Get-Member -InputObject $object -MemberType Properties -Name $propertyName))) {
        return $null
    }

    $object.$propertyName
}

$value = Get-OptionalPropertyValue $foo "bar" # if $foo.bar exists, $value will contain its data; else $value will be $null

当您_really_应该只能够写:

$value = $foo?.bar # if $foo.bar exists, $value will contain its data; else $value will be $null

拜托,如果您只能从实现的提案中针对房地产,那么它将为许多人节省很多痛苦。

我开始研究一下,发现了一些问题。

让我们考虑???=

$x ?= 1$x ?? 1似乎很简单。 但是,由于它们是运算符,因此我们需要支持$x?=1$x??1

问题在于$x?$x??都在PowerShell中验证变量名。 消除歧义的唯一方法是${x}?=1${x}??1

对于条件成员访问?.?[] ,我们还必须拥有${x}?.name${x}?[0]
对于这些,将不支持$x ?.name$x ?[0]

另一种选择是引入重大更改,并且根本不允许在变量名中使用?

你怎么看? @ mklement0

/ cc @rjmholt @ daxian- dbw @JamesWTruher

我从不特别喜欢,也不了解为什么?是有效的变量名。 它为$Safe?类的名称打开了大门,当以$IsSafe书写时,通常更清晰,更不尴尬。

我认为变革可能已经很长时间了。

它为$ Safe之类的名称打开了大门。 通常写为$ IsSafe时,它通常更清晰,更不尴尬

有趣的是,我表达了相反的观点。 这是经典的Scheme / Ruby传统。

if ($questionMark?)
{
    Write-Host "That's silly!"
}

三元运算符不是有同样的问题,即解决方案是要求在运算符周围使用空格或在变量名周围使用花括号,以便它能够正确处理以?结尾的变量?

我希望引入这些运算符后,PowerShell将优先识别?作为$x?[0]等语法中运算符的一部分,这样使用变量名以?结尾的用户${x?}?[0]$x?.Name${x?}?.Name同上。

另一种选择是引入重大变化,不允许吗? 在变量名中。

👍鉴于这是一个主要的版本更改(从6到7),因此我认为值得在此进行重大更改。 我希望GitHub可以进行更好的代码搜索,以查看有多少个以?结尾的PS变量实例。 我怀疑这没有那么大的影响力,但可以很好地针对GitHub进行验证。 在使用PowerShell的14年中,我从未使用过?作为变量名,但是也许就是我。 :-)

那艘船已经航行很久了,今天在脚本中使用以?结尾的变量我不会感到惊讶。

在这一点上,如果涉及这些新的运算符时,如果我们只是简单地将?解析?结尾的变量名的人将需要使用{}或空格(允许空格)在这些运算符中使用这些变量。

在这种情况下,听到“那艘船已经航行”这句话很奇怪。 这是主要版本的更改。 我认为,在此进行更改并非没有道理。

@rkeithhill我在个人中使用了它,但是认为协作工作尚不清楚,因为对于程序员来说,将符号作为变量名的一部分是一种反直觉的事情(类似于使用表情符号作为变量)

@KirkMunro具有“优先解析”的功能听起来像是漏洞的门户。

@ vexx32 :对于重大更改而言,这不是不合理的,因为这是主要的版本更改; 但是,此类突破性更改的标准应该保持很高,并且我认为这不会接近通过该标准,因为用户可以使用以?结尾的变量,只要他们使用{}用来标识变量名的附件。

请注意,您也可以使用以?结尾的属性名称。 当前,如果您尝试在PowerShell中查看此类属性而不用双引号引起来,则将出现解析器错误。

例如:

PS C:\> $o = [pscustomobject]@{
    'DoesItBlend?' = $true
}
PS C:\> $o.DoesItBlend?
At line:1 char:15
+ $o.DoesItBlend?
+               ~
Unexpected token '?' in expression or statement.
+ CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken

PS C:\> $o.'DoesItBlend?'
True

我有点惊讶今天没有解析(为什么不解析?),但是无论如何,对于此类属性,您需要将其名称括在引号中,在这种情况下,您可以在引号后加上三元,空值-coalescing等运算符,不带空格,它将正常工作。 我发现此语法与${x?}?.name非常相似,并且我同意您可以使用此类变量/属性名称的立场,但此类名称可能需要额外的语法或空格才能使用三元或null -*运算符。

@KirkMunro如果他们想要深奥的变量名,不会阻止人们继续使用变量界限。 我确实同意其他观点,因为我想知道该行为的当前用法是什么。

使用PowerShell 7的人们可能已经很热衷,并且会意识到重大的变化。 没有使用它们的人仍在使用<= v5.1,并且会持续很长时间; 直到msft将其从Windows 10中删除(从不)。

@KirkMunro当然可以,但是将其从标准的允许变量字符中删除并不会阻止用户仅使用${Valid?}作为变量名。 因为无论如何他们都必须对这些运算符执行此操作,所以我认为最好使其保持一致,而不是让?成为仅有时被视为变量名一部分的字符。

那已经是一个巨大的变化,我认为最好至少对此保持一致并一路走下去,而不是引入另一种歧义。 🙂

@adityapatwardhan我认为,最好将语言删除?作为有效的可变字符。 它将很容易以熟悉的语法启用null-soak / -coalesce和三元运算符,从而为脚本创作过程增加了许多人体工程学。

@KirkMunro具有“优先解析”的功能听起来像是漏洞的门户。

@ TheIncorrigible1 :如果执行不正确,任何代码都可以打开漏洞的大门。 我只是在谈论一个简单的单字符前瞻,以标识PowerShell在解析未包含在变量范围内的变量名并遇到?字符时是遇到三元运算符还是null- *运算符。 这并不复杂,也没有打开比其他任何代码更改都更多的错误的大门。

使用PowerShell 7的人们可能已经很热衷,并且会意识到重大的变化。 没有使用它们的人仍在使用<= v5.1,并且会持续很长时间; 直到msft将其从Windows 10中删除(从不)。

@ TheIncorrigible1 :您对该陈述有何依据/证据? PowerShell 7处于预览阶段,因此今天仅供爱好者使用。 那是给定的。 但是,除预览版之外,如果PowerShell 7或更高版本提供公司所需的引人注目的功能,同时支持所需的功能,则将使用这些版本。 如果PowerShell 7与操作系统一起安装,则尤其如此。 发烧友唯一要注意的一点是那些对PowerShell 7不会带来什么业务需求的组织。

那已经是一个巨大的变化,我认为最好至少对此保持一致并一路走好,而不是引入另一种歧义。 🙂

@ vexx32可以扩展它。 如果您使用??作为变量名,那将是一个巨大的变化,但是与使用以单个?结尾的变量相比,这种可能性要小得多。 除此之外,如何引入null- *运算符,同时仍支持?作为当今标准的允许的可变字符中断脚本?

以我的经验,打破常做的改变(这似乎是事实)经常会被拒绝,围绕它们的讨论/争论只会减慢大幅度完成事情的过程,通常情况下,放慢速度是很有必要的,因为如果您要提出重大变更,则需要有证据来评估影响并为此类变更辩护。 今天很难收集这些证据。 我之所以这样说,是因为我会选择立即完成功能,而不是每天都在争论一个不错的重大更改。

我从不在变量名中使用? ,我也不会。但是,我希望有些人这样做,因为它可以在脚本中很好地阅读,并且为PowerShell 7设置不必要的进入障碍只会减慢采用速度,尤其是当许多使用PowerShell的人不是更习惯于进行重大更改的开发人员时。

无论如何,我的意图不是放慢这个过程,而是相反。 但是我已经分享了我的想法和经验,所以我不会在这里进一步评论我们是否应该在这里寻求突破性的改变。

考虑这个人为的例子:

$ValueIsValid? = @( $true, $false, $false, $true )

$ValueIsValid?[0]
# old behaviour? gets `$true`
# new behaviour? gets nothing, because the `?[0]` is interpreted as a null-conditional access.

此行为将__已经_与提议的更改中断。 我宁愿使用一个一致,干净的中断,而不是一个令人迷惑的中断,该中断需要半页的说明来列出所有可能的异常,以及?何时何地突然无效,而仍然无效。

@KirkMunro,如果PowerShell 7或更高版本提供公司需要的引人注目的功能,而在支持他们所需的功能的同时,他们将使用这些版本。 如果PowerShell 7与操作系统一起安装,则尤其如此。

在少数几个财富50强企业中工作过,拥有数十万员工的员工,即使要摆脱OS上的默认设置也是一个挑战(即,迁移到v5.x)。 我还没有看到任何地方采用Core ; 他们宁愿使用Python来实现交叉兼容性。 很少有启用Windows可选功能的任务。

我认为公司在寻找新技术或新语言版本来解决他们的问题时,会利用自身的知识和员工的知识基础进行合作。 有些内容永远保留在v2上,并且永远不会触碰Win10 / Server16 cmdlet,这将使生活变得更加轻松,这是完全令人满意的。

我所有这些的观点是,这些功能并不是一成不变的。 如果您使一种语言更符合人体工程学,那么对该工具感兴趣的人将会越来越多地采用该工具,以更快地解决他们遇到的问题。 (请参阅:C#和具有更多/更好语言功能的受欢迎程度的增长)

关于以?结尾的变量-由于自动变量$ ,这可能是一个重大突破

@lzybkr我怀疑最好的处理方式是特殊情况,例如$^$$已经存在的情况。

@lzybkr @ TheIncorrigible1据我所记得,这些变量中的_all_在记号化程序中明确地用特殊字符表示。 $^ _already_不是有效的变量名。 在开始查找标准变量名称之前,令牌生成器对所有这些都有特殊情况。

唯一的解决方案是使用¿

$ silly?¿= 1

我不知道史蒂夫,我坚定地参加了这一活动。

$silly?‽=1

@lzybkr-就我所知, $? $$$^以特殊方式处理。

https://github.com/PowerShell/PowerShell/blob/8b9f4124cea30cfcd52693cb21bcd8100d39a796/src/System.Management.Automation/engine/parser/tokenizer.cs#L3001 -L3004

总而言之,我们有这四个选项

  • 重大更改-不允许在变量名称中使用?
  • 最好将?.作为运算符。 这意味着以?结尾的变量名称必须使用${variablename}语法。 仍然是一个巨大的变化。
  • 更喜欢旧的行为。 这意味着要使用?.?[] ,必须使用${variablename}?.property 。 没有重大变化,但使使用新运算符变得笨拙。
  • 不要实施新的运算符。

我个人不喜欢第一个和最后一个。

这是主要的版本更改。 我认为,在此进行更改并非没有道理。

我认为重要的一点是,主要版本正在增加,以表示已准备好替换Windows PowerShell,这表示它表示兼容性,而不是损坏。 从原始公告

请注意,主要版本并不意味着我们将进行重大的重大更改。

选项1和2不同吗? 还是在变量后面没有空值或三元运算符的情况下,还是允许变量使用?

如果是这样,我认为在没有大量回溯的情况下处理标记化/解析逻辑可能会遇到很多麻烦。 当变量以?结尾时,这可能导致性能下降。

鉴于我所看到的,选项2似乎是普遍的偏好,我不确定我是否理解不愿意在此处进行重大更改。 以这种方式进行操作,无论如何会积极地阻止在变量名中使用? ,只需简单地介绍一些不使用变量名就无法使用的情况。

我认为随着变更的进行,这是一个相当小的变更,在我看来,中断在变量名称中可以使用和不能使用的名称的一致性可能是更糟糕的选择。

这些东西应该有适用于几乎所有情况的明确规则。 目前是这种情况。 我们建议在这里弄混一些水,使行为_less_清楚。 我看不到有什么好用的,除了也许包含?变量名使用得更少是因为它们在某些情况下更难使用-(有效地)使我们回到了选项1默认情况下,几乎...所以我看不出有什么特别的理由不立即休息,而只是避免模棱两可的行为。

@ vexx32 1和2之间有细微差别。

对于#1,我们不允许?一起用于变量名。 这意味着$x? = 1$x?? = 1 $x?x?x = 1将不会解析。

对于#2, $x? = 1仍然有效,但$x?.name等效于${x}?.name 。 这只会中断变量名,并在末尾带有? ,这些变量名正在访问成员。 因此, $x? = 1$x?? = 1 $x?x?x = 1仍然有效。

是的,我有点担心那里的标记化开销。 可能有必要同时实现这两种可能性,并在使用大量相似变量名称的脚本上做一些基准测试。

我的选择绝对是选项1。。。对我而言,一次性的重大更改至少比处理在将来如何解析时出现的不一致更可取。

如果是这样,我认为在没有大量回溯的情况下处理标记化/解析逻辑可能会遇到很多麻烦。 当变量以?结尾时,这可能导致性能下降。

我对此可能性表示关注。 对于我来说,使用?作为有时的令牌分隔符感觉像是锯齿状的一行。

如果是这样,我认为在没有大量回溯的情况下处理标记化/解析逻辑可能会遇到很多麻烦。 当变量以?结尾时,这可能导致性能下降。

这取决于您如何实施。 只要您采用先行方式而不是分词然后备份的方式,就可以了。 当遇到变量名称中的? ,您可以向前看下一个字符,然后根据下一个内容决定如何“包装”变量标记。 这没有什么大麻烦,也不需要回溯或导致明显的性能下降。

@ PowerShell / powershell-committee今天回顾了这个,我们有一些想法:

  • 无论我们做什么,我们都会对脚本集进行一些分析,以了解人们使用变量名中的?频率
  • 我们中的一些人有一个假设(其他人想验证),即在变量名中使用?用户可能不是较高级的用户(因为我们同意在这个会议室中,所以我们会远离它,因为可能出现的潜在问题)。 另一方面,使用此处描述的功能的任何人都将能够理解稍微复杂的语法(例如${foo}?.bar )。 因此,我们首选选项3,因为它可以避免破坏这些经验不足的用户的更改。 (但同样,我们将根据第一个项目符号对其进行验证。)
  • @ daxian-dbw提出了一个很好的观点,即当脚本编写者将$foo${foo}用法混合使用时,使查找所有变量引用变得更加困难。 但是,我们同意可以在工具中解决此问题(例如VS Code PowerShell扩展),并且@JamesWTruher这样的用户使用Vim这样的编辑器可以轻松地在其搜索语法上进行匹配。

当脚本编写者混合使用$ foo和$ {foo}时,很难找到所有变量引用

这取决于AST是否根据是否看到花括号来区分变量名称。 我怀疑任何使用PowerShell AST的工具在这里都不会出现括号问题。

但是对于正则表达式,这当然意味着您必须更加了解: varname -> \{?varname\}?

@rjmholt在这里进行了分析。 快速分解:在将近40万个脚本中,变量名超过22,000,000(不是唯一),只有〜70个以?结尾的唯一变量。

谢谢,@mburszley -对我来说,它使一个桶3:不确定灰色地带的变化,并与需要负担的用户{...}是非常不幸的,平行的不幸需要$(...)左右exit / return / throw&&||上下文中的语句

借用该问题的推理:

如果我们强制在变量名周围使用{...} ,以便可以使用?.

  • 新用户将不会期望{...}并且不一定会知道_it_是以下操作失败时所必需的(可能是_undetected_,除非Set-StrictMode -Version 2或更高版本有效): $o = [pscustomobject] @{ one = 1 }; $o?.one

  • 即使用户_do_知道需要{...}

    • 由于对直觉的需求,他们有时会忘记使用它。
    • 当他们记得时,这种看似人为的要求将是持续不断的挫败感,特别是因为{...}很难键入。

该问题已被标记为已修复,并且在1天内没有任何活动。 出于清洁目的而关闭。

@rjmholt ,从https://github.com/PowerShell/PowerShell/issues/10967#issuecomment -561843650引用你:

正确的方法是不破坏我们解析现有有效语法的方式。 再次说明,不仅是我们做出决定支持少量野生程序的情况,我们还做出了许多重大突破,以至于我们认为赞成者胜过了反对者(并非我个人同意)与所有这些,或那些证明或不证明别人的理由)。 问题在于,更改令牌生成器或解析器的某些方面意味着两个PowerShell版本将不再为某些脚本生成相同的AST,因此两个PowerShell将以不同方式“看到”相同的脚本。 这意味着我们甚至无法以相同的方式读取语法,因此没有可编写的PSScriptAnalyzer规则来解决可能存在的问题。 PSScriptAnalyzer将看到从一个PowerShell版本到另一个PowerShell版本的不同语法。

与C#不同,PowerShell无法将其他语法预先编译为兼容格式以供以后执行,这意味着语法更改将绑定到运行时。 一旦PowerShell中断了语法,我们将减少可在不同版本上运行的脚本的数量,这意味着用户被迫编写两个脚本,这对于shell和脚本语言而言是一个严重的问题。 PowerShell应该具有足够的动态性,可以克服一个脚本中逻辑的所有差异,但是语法是我们拥有的不可转让的静态事物。 并且在前后都有效的情况下进行语法更改尤其有害,因为没有简单的方法可以检测到它,也没有警告,即使在两种环境中都执行后,用户也可能不知道发生了不同的行为。

总的来说,我可以理解,这样的更改可能会带来很大的问题,只有在优点大于缺点的情况下才应进行更改,其中一个重要因素是现有代码的中断次数。

我们减少了可以针对不同版本使用的脚本数量
意味着用户被迫编写两个脚本

我们在这里讨论的是_new_语法功能( ?. ),因此根据定义,使用_cannot_的脚本(有意义地)在较旧的版本上运行-除非您专门添加条件以提供与旧版兼容的代码路径,但这似乎不值得-您可以继续使用原有功能。

是的,在使用$var?作为变量名的旧脚本中对$var?.foo的解释会中断,但是:

  • 绝对不应将?作为标识符_的一部分,而不能将其包含在{...} _中。

  • 由于这样做是出乎意料的,甚至对于许多人来说甚至是未知的,所以从个人经验来看,这样的变量名在野外极为罕见,但是@mburszley的分析提供了更切实的证据

    • 甚至Ruby仅在标识符的_end_处只允许? (和! ),并且只存在_method_标识符,而且我怀疑大多数Ruby用户都应该意识到,他们应该_not_假定其他语言也支持同样的事情。

因此,务实地说:

  • 现有的绝大多数代码都不会受到影响-只会遇到诸如$var?.foo类的令牌。

  • 如果您使用新的语义编写$var?.foo ,那么可以,在旧版本上运行它可能会导致不同的行为(而不是以明显的方式破坏),具体取决于有效的严格模式-但_you应该始终强制按照预期的方式运行代码所需的最低版本_( #requires -Version ,模块清单键)。

总而言之,对我来说,这是第3项更改的一个清晰案例:技术上具有重大突破的更改,它破坏了很少的现有代码,同时提供了真正的好处(或者,避免了由于意外行为而导致的长期困扰)。

@ mklement0我们正在针对PS7.0进行空条件访问实验。 也许您可以打开一个新问题来继续进行7.1的讨论?

听起来不错,@ SteveL-MSFT-请参阅#11379。

我鼓励这里的每个人在此再次表示支持(或喘气,不支持)。

此页面是否有帮助?
0 / 5 - 0 等级