原谅标题,我不确定如何解释我所看到的内容。
示例代码:
$SampleValues = 1..5
$Result = "" | Select ID
$Results = @()
$SampleValues | %{
$Result.ID = $_
$Results += $Result
}
$Results
这非常简单:
预期结果是1,2,3,4,5,但运行时返回5,5,5,5,5
这是一个来自更复杂脚本的准系统示例,我试图弄清楚结果是什么原因。在每次迭代中,已添加到$ Results的所有元素都将其值更新为最新值。我已经测试了强制所有内容到$ Script:或$ Global:scope并得到相同的结果。
我找到的唯一解决方案是将$ Result声明移动到循环中。
$SampleValues = 1..5
$Results = @()
$SampleValues | %{
$Result = "" | Select ID
$Result.ID = $_
$Results += $Result
}
这样可行(你的结果会得到1,2,3,4,5)。它看起来像$ Results只是持有多个对单个$ Result对象的引用,但是为什么将它移动到循环中可以解决问题呢?在这个例子中,$ Result是一个字符串,所以也许每次迭代都会创建一个新对象,但即使我强制$ Result为一个整数(由于整数不像字符串那样是不可变的,因此不应该重新创建一个新对象)仍然解决了问题,我得到了我期望的结果。
如果有人对这个解决问题的确切原因有任何了解,我会非常好奇。我有很多替代方案可以实现,但没有明确地理解为什么这样做会让我烦恼。
答案 0 :(得分:8)
它通过进入循环来解决问题,因为那时你每次都要创建一个新的$ Result对象,而不是在同一个上更改一个值(在数组中引用了5次)。
它与您使用"" | Select ID
还是123 | Select ID
没有任何关系,因为它只是PSCustomObject上的一种属性,它仍然是引用类型而不是值类型。
请记住,Powershell内部都是.NET。这里有一些C#类似于Powershell在你的第一个例子中所做的事情,结果是所有5个(希望你知道C#):
var SampleValues = new []{1,2,3,4,5};
var Result = new CustomObject(){ ID = "" };
var Results = new List<Object>();
foreach (var _ in SampleValues) {
Result.ID = _;
Results.Add(Result);
}
希望你能看到var Result = new CustomObject(){ ID = "" }
循环中移动foreach
如何使其更好地工作,同样的概念在Powershell中也适用。
答案 1 :(得分:1)
在这个示例中,$ Result是一个字符串,所以它可能每次迭代都会创建一个新对象,但即使我强制$ Result为一个整数(由于整数不是因为它不能重新创建一个新对象像字符串一样不可变)它仍然解决了问题,我得到了我期望的结果。
实际上,在您的示例中,$ Result不是字符串。它是一个通用对象,其中一个属性(ID)是一个字符串。
Powershell变量有两种类型 - 值和引用。字符串或整数按值传递,对象通过引用传递。通过addind $ result到$ results 5次,你可以获得5个参考结果,而不是5个不同的对象。
如果我们将$ result.id属性(整数/值类型)添加到$ results而不是$ result对象,我们得到:
$SampleValues = 1..5
$Result = "" | Select ID
$Results = @()
$SampleValues | %{
$Result.ID = $_
$Results += $Result.ID
}
1
2
3
4
5