为什么 PowerShell 在仅添加一个 PSCustomObject 时将 ArrayList 转换为 PSCustomObject? [英] Why does PowerShell convert an ArrayList to a PSCustomObject when adding only one PSCustomObject?

查看:34
本文介绍了为什么 PowerShell 在仅添加一个 PSCustomObject 时将 ArrayList 转换为 PSCustomObject?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个代码,它获取符合条件的所有文件.对于找到的每个文件,我创建一个只有少数文件 ($Obj) 值的自定义对象,然后将其添加到 ArrayList ($Files).这个 ArrayList 然后返回给调用函数并存储在它的变量 ($OldFiles) 中.

I am writing a code where it gets all files matching a criteria. For each file found, I create a custom object with only few of the file's ($Obj) values and then add this to an ArrayList ($Files). This ArrayList is then returned to the invoking function and stored in the variable of it ($OldFiles).

当没有找到文件或找到多个文件时,这可以正常工作.但是,如果只找到一个文件,ArrayList 会将自身转换为 PSCustomObject,然后它会抛出异常,因为返回无法将 PSCustomObject 转换为ArrayList.

This works fine when no files or more than one are found. However, if only one file is found, the ArrayList converts itself to an PSCustomObject and then it throws an exception as the return cannot convert PSCustomObject to ArrayList.

奇怪的部分是,如果我在 return 语句之前添加简单的行 "$Files.getType()",则没有错误并且 ArrayList 保持为一个 ArrayList.

The curious part is, if I add the simple line "$Files.getType()" before the return statement, there is no error and the ArrayList remains as an ArrayList.

基本上,这行不通:在第一个函数中简化:

Basically, this will not work: Simplified in the first function:

$Files = [System.Collections.ArrayList]@()
$Files.Add($Obj) > $null
return $Files

这就是它在 top 函数中的样子:

And this is how it looks in the top function:

$OldFiles = [System.Collections.ArrayList](Check-OldFiles -Path $Directory -Age $Age -FilesOnly:$FilesOnly -Recurse:$Recurse -UserCreationTime:$UserCreationTime)

但这工作正常:在第一个函数中简化:

But this works fine: Simplified in the first function:

$Files = [System.Collections.ArrayList]@()
$Files.Add($Obj) > $null
$Files.getType()
return $Files

这就是它在 top 函数中的样子:

And this is how it looks in the top function:

$OldFiles = [System.Collections.ArrayList](Check-OldFiles -Path $Directory -Age $Age -FilesOnly:$FilesOnly -Recurse:$Recurse -UserCreationTime:$UserCreationTime)

为什么会发生这种情况?

Why could this possibly be happening?

PowerShell 版本是 5.0

推荐答案

澄清、警告(@Bender the Greatest)和解释(@Phragos):

Clarification, caution (both @Bender the Greatest) and explanation (@Phragos):

  1. @() 不会包装"它的内容.它将其内容转换为 Array.如果它的内容已经是一个 Array 那么它会按原样返回它.如果它是一个值或单个对象,则将其复制到一个元素 Object[] 中.如果它是某种其他形式的集合(例如 ArrayList),那么它将每个元素复制到一个 Object[].(注意:@()"是一个空的Object[].)

  1. @() does not "wrap" its content. It converts its content to an Array. If its content is already an Array then it returns it as is. If it is a value or a single object then it copies it into a one element Object[]. If it is some other form of collection (e.g. ArrayList) then it copies each element into an Object[]. (Note: "@()" is an empty Object[].)

不要使用 ,(...) 来强制 ... 成为 Array.输出对象(显示、文件、打印机等)时的自动展开行为可能会产生误导.

Don't use ,(...) to force ... to be an Array. The automatic unwrapping behaviour when outputting objects (display, file, printer, etc...) can be misleading.

$myCollection = ,(get-process)

不会将 $myCollection 与进程的 Array 分开,除非只有一个进程(例如来自 get-process -name).相反,$myCollection 将是包含整个输出的单个元素的 Array(单个对象或输出对象的 Array).这个事实被格式输出 cmdlet(Format-TableFormat-ListFormat-Wide)掩盖了,它检查接收到的对象,如果有 Arrays,请解开它们.(更多细节PowerShell 格式化和输出的真正工作原理).

does not leave $myCollection with an Array of processes unless there is only one process (such as from get-process -name). Instead, $myCollection will be an Array of a single element containing the whole output (either a single object or an Array of the output objects). This fact is obscured by the format output cmdlets (Format-Table, Format-List and Format-Wide) which examine the objects received and, if any are Arrays, unwrap them. (More detail How PowerShell Formatting and Outputting REALLY works).

您说但如果我这样做,它就可以正常工作":

You say "But if I do this it works fine":

$myCollection | foreach { $_.starttime }

我说,是的,你会得到一个开始时间列表不是你想的那样.在上面,$myCollection 被解包并且每个元素被发送到管道中到 foreach.只有一个元素是 Array,所以 foreach 循环一次,$_ 设置为 Array过程.由于 Array 是一个集合但没有 starttime 成员,PowerShell (V3+) 执行 member enumeration 创建一个 Array 由 starttime 的值组成$_ 的每个元素的成员.这个 Array 然后被解包并发送到管道中.

and I say, yes, you get a list of starttimes BUT not the way you think. In the above, $myCollection is unwrapped and each element is sent into the pipeline to foreach. There is only one element which is an Array so the foreach loops once with $_ set to an Array of processes. Since Array is a collection but has no starttime member, PowerShell (V3+) performs a member enumeration creating an Array consisting of the values of the starttime members of each element of $_. This Array is then unwrapped and sent into the pipeline.

你说那又怎样,效果是一样的";我回答说,是的,直到你尝试更复杂的事情:

You say "So what, the effect is the same" and I reply, yes until you try something more complicated:

$myFiles = ,(gci -file *)
$myFiles | foreach { $_.name+' '+$_.length }

预期输出是文件名和长度的列表.实际输出是名称列表、空行和文件数.为什么?$myFiles 是一个包含文件列表的元素的 Array,所以 foreach 循环一次,$_ 就是这个列表.Array 没有 name 成员,因此 PowerShell 执行 member enumeration 生成名称的 Array.接下来,一个空格的String被添加(连接)到这个Array.最后,添加 $_.length.由于 Array 确实有一个长度成员,所以这个值(一个 Int32)被添加到名称的 Array' '.这个 Array 然后被解包并发送出去.

Expected output is a list of file names and lengths. Actual output is a list of names, a blank line and the number of files. Why? $myFiles is an Array of one element containing the list of files so foreach loops once with $_ being this list. An Array has no name member so PowerShell performs a member enumeration producing an Array of names. Next, a String of one space is added (concatenated) to this Array. Finally, $_.length is added. Since an Array does have a length member, this value (an Int32) is added to the Array of names and ' '. This Array is then unwrapped and sent out.

总结,如果您希望某些可能是也可能不是 Array 的东西绝对是 Array,请不要使用 ,(...).始终使用@(...),这就是它的用途.

Summary, if you want something that might or might not be an Array to definitely be an Array, don't use ,(...). Always use @(...), that's what it's for.

然而,在其他情况下,使用 ,(...) 是合适的,但仍然会造成混淆.

However, in other situations, the use of ,(...) is appropriate but can still create confusion.

解析命令行时,空格分隔标记而不是参数.因此,以下内容是等效的:

When parsing the command line, spaces separate tokens not arguments. Therefore, the following are equivalent:

1,2,3

1  , 2,      3

当使用前置的 , 创建一个包含一个项目的数组时,这可能会让您感到困惑.假设您有一个将 Array 作为参数的 cmdlet,但您只想提供一个本身就是 Array 的元素.例如,

This can trip you up when using the prepended , to create an array of one item. Say you have a cmdlet that takes an Array as a parameter but you want to supply only one element that itself is an Array. For example,

new-object collections.arraylist ,(1,2,3)
New-Object : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'ComObject'. Specified method is not supported.
At line:1 char:12
+ new-object collections.arraylist ,(1,2,3)
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [New-Object], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.NewObjectCommand

由于 , 之前的空格基本上被解析器忽略(, 是单个字符标记,因此不需要分隔),因此被解析为:

Since the space before the , was essentially ignored by the parser (, is a single character token so doesn't need separation), this was parsed as:

 new-object ("collections.arraylist",(1,2,3))

所以在这种情况下应该是:

so in this case it should be:

 new-object collections.arraylist (,(1,2,3))

注意:@(1,2,3) 不起作用,因为 1,2,3 已经是一个 Array.

Note: @(1,2,3) would not work since 1,2,3 is already an Array.

Powershell 中的 return 语句与函数式语言不同,因为它不是产生输出的唯一方式.每个产生 void 以外的值的表达式都被发送到管道中,即收集返回(展开数组的值并单独收集元素).return 使函数在生成值后返回(它不返回"值).我假设函数的不可见部分没有生成任何值.但是,将 $Files.gettype() 放入函数会导致生成一个值(System.RuntimeType).因此,该函数总是比以前多返回 1 个对象.我必须假设没有测试 0 个匹配文件的情况(该函数已经适用于这种情况,对吗?)因为现在我们回到了一个对象,因此该函数返回一个值".再次(System.RuntimeType).我不知道返回的对象是如何使用的,但没有提到由 ArrayList 的第一个元素是 System.RuntimeType 而不是 代码>PSCustomObject.据推测,用法不介意为不存在的属性获取 $null(只有 [String]Name 和 [String]Fullname 对 System.RuntimeTypeSystem.IO 都是通用的.FileInfo) 并且 Attributes 没有引起类型冲突(System.RuntimeType.AttributesSystem.IO.FileInfo.AttributesSystem.Enum[Collections.ArrayList].Attributes 的值不是有效的 IO.FileInfo.Attributes 值,或者 PowerShell说).

The return statement in Powershell is not like in functional languages in that it is not the only way to produce output. Every expression that produces a value other than void is sent into the pipeline, i.e. collected for return (values which are arrays are unrolled and the elements collected individually). return causes the function to return after the value is generated (it does not "return" the value). I am assuming that there are no values being generated by the the unseen parts of the function. However, putting $Files.gettype() into the function causes a value to be generated (a System.RuntimeType). Therefore, the function always returns 1 more object than before. I have to assume that the case of 0 matching files was not tested (the function already works for this case, right?) because now we are back to one object so the function returns a "value" again (System.RuntimeType). I don't know how the returned objects are being used but there is no mention of problems caused by the first element of the ArrayList being a System.RuntimeType instead of a PSCustomObject. Presumably, the usage did not mind getting $null for the properties which don't exist (only [String]Name and [String]Fullname are common to both System.RuntimeType and System.IO.FileInfo) and that Attributes caused no type conflicts (System.RuntimeType.Attributes and System.IO.FileInfo.Attributes are both System.Enum but the value of [Collections.ArrayList].Attributes is not a valid IO.FileInfo.Attributes value, or so PowerShell says).

† 赋值语句 属于void 类型,而赋值表达式(表达式中的赋值)具有非void 类型.因此,

† Assignment statements are of type void while assignment expressions (assignments within an expression) have non-void type. Thus,

$a = 5;       // void
($a = 5)      // int
($a = 5);     // also int
if ($a = 5){} // valid
// the assignment is evaluated as part of a conditional expression (value 5 -> $true)

这篇关于为什么 PowerShell 在仅添加一个 PSCustomObject 时将 ArrayList 转换为 PSCustomObject?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆