【问题标题】:Variable changes value without re-assignment变量改变值而不重新赋值
【发布时间】:2019-05-02 01:06:57
【问题描述】:

我有一个变量,它通过我更新不同变量的值来改变它的值。

我正在尝试创建自己的对象并尝试使用我在网上找到的一些代码,我偶然发现了这个问题,无法理解这是如何发生的。

准备工作:

$a = "" | select First,Last #This just creates a custom object with two columns.
$b = @() #This is just an empty array to fill later on with the values of '$A'

$a.first = "Alpha"
$a.last = "Bravo"
$b += $a

$a.first = "Charlie"
$a.last = "Delta"
$b += $a

应该发生什么:

第一个 最后一个 ----- ---- 阿尔法布拉沃 查理三角洲

但实际结果是:

第一个 最后一个 ----- ---- 查理三角洲 查理三角洲

这里有什么问题?

【问题讨论】:

  • 您没有添加 $A 的副本 ...您正在添加对 $A 的引用。[grin] 所以当它更改 references 时指向它也会改变。使用.Clone() 或制作新对象以添加到您的收藏中。
  • 我不会亲自使用克隆,我会在对象中创建自己的克隆方法,该方法从旧对象中初始化的值构造一个新对象并返回该新对象。根据我在旧计算机科学课程中学到的知识.Clone() 不一定会做深拷贝……但也不一定会做浅拷贝。
  • @Lee_Dailey 在自定义对象中没有 .clone 方法。您还可以解释一下制作新对象的意思吗?我问是因为我使用自定义对象而不是数组来存储集合,但它也将第一个对象存储为参考。
  • @Yeahish - 我忘记了自定义对象没有 .Copy().Clone() 方法。 [blush] 在这种情况下,您每次都需要制作一个新的。因为Select-Object 方法是SLOW,你可能会使用正确的方法=[PSCustomObject]@{}
  • @Yeahish - Select-Object 方法涉及更多代码,可以处理许多不同的用例。另外,它需要一个人通过管道发送东西......这总是比更直接的方法慢。 PSCO 技术更简单,因为它专门用于制作对象并且不需要使用管道。

标签: powershell


【解决方案1】:

正如Lee_Dailey 指出的那样,您将引用 添加到存储在$a 中的自定义对象([pscustomobject][1]) 实例到数组@ 987654327@(假设 [pscustomobject] 是一个引用类型),并且您正在更新相同的自定义对象,因此您最终得到:

  • 指向同一个对象的2个数组元素...
  • ...其属性值是您最后分配的值。

最直接的解决方法是在分配新属性值之前创建$a副本,这可以使用.psobject.Copy() 来完成,它会创建一个浅层克隆:

...
# Create a copy of $a to create the next array element.
$a = $a.psobject.Copy()

$a.first = "Charlie"
$a.last = "Delta"
$b += $a

当然,您可以避免这个问题并改用自定义对象 literals (PSv3+),它每次都会创建一个新实例:

$b = @()

$b += [pscustomobject] @{ 
  first = "Alpha"
  last = "Bravo"
}

$b += [pscustomobject] @{ 
  first = "Charlie"
  last = "Delta"
}

顺便说一句:使用+= 增长数组效率低下,因为每次都必须在后台创建一个新数组;仅通过少量添加/迭代可能不会引起注意,但如果数量较多,最好使用列表数据类型,例如 [System.Collections.Generic.List[object]]

$b = New-Object System.Collections.Generic.List[object]

$b.Add([pscustomobject] @{ 
  first = "Alpha"
  last = "Bravo"
})

$b.Add([pscustomobject] @{ 
  first = "Charlie"
  last = "Delta"
})

[1] PowerShell 中的自定义对象:

[pscustomobject](实际上与[psobject] 相同)是 PowerShell 的“属性包”类型,它允许您构造对象ad hoc,而无需提前声明不同的 .NET 类型;从 .NET 的角度来看,给定的自定义对象的类型始终为 System.Management.Automation.PSCustomObject,尽管特定的动态附加属性可能会有所不同,设计上有所不同。

Select-Object (select) 也输出 [pscustomobject] 实例,尽管它们通过 PowerShell 的 ETS (Extended Type System) 报告自定义类型名称作为它们的主要类型名称:

PS> ("" | select First,Last).pstypenames
Selected.System.String  # custom type name to reflect the type of the *input*
System.Management.Automation.PSCustomObject # the true type name
System.Object # the name of the base type

上述创建自定义对象的方法已过时,然而,PSv3+ 支持直接构造的文字[pscustomobject] @{ ... } 语法,这具有能够初始化 属性作为同一语句的一部分,并且比Select-Object(以及New-Object更快

# PSv3+ equivalent of the above 
# (except for the custom 'Selected.System.String' type name),
# allowing you to also initialize the properties.
[pscustomobject] @{
   First = $null  
   Last = $null 
}

注意:在 PSv2 中,您可以使用 New-Object PSCustomObject -Property @{ ... } 达到类似的效果,但附加属性的顺序通常不会反映定义顺序。

请注意,您也始终可以在 PowerShell 中使用 New-Object cmdlet 或在 PSv5+ 中使用语法 [<type>]::new([...]) 来实例化常规 .NET 类型。 您可以使用相同的方法来实例化使用 class 关键字声明的 PSv5+ custom classes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    • 1970-01-01
    • 2015-04-28
    • 1970-01-01
    相关资源
    最近更新 更多