PowerShell类型加速器:PSObject vs PSCustomObject

15

PowerShell v3.0引入了PSCustomObject。它类似于PSObject,但更好。除了其他改进(例如属性顺序被保留),从哈希表创建对象变得更加简单:

[PSCustomObject]@{one=1; two=2;}
现在看来,这个声明是显而易见的:
[System.Management.Automation.PSCustomObject]@{one=1; two=2;}

会以相同的方式工作,因为PSCustomObject是完整命名空间+类名称的“别名”。但我收到了一个错误:

无法将类型为“System.Collections.Hashtable”的值“System.Collections.Hashtable”转换为类型“System.Management.Automation.PSCustomObject”。

我列出了两种对象类型的加速器:

[accelerators]::get.GetEnumerator() | where key -Like ps*object

    Key            Value
    ---            -----
    psobject       System.Management.Automation.PSObject
    pscustomobject System.Management.Automation.PSObject

并且发现两者都引用了同一个PSObject类——这意味着使用加速器可以做很多其他事情,而不仅仅是缩短代码。

我对此问题的疑问是:

  1. 您有一些有趣的示例,说明使用加速器与使用完整类型名称之间的差异吗?
  2. 在可用加速器的情况下,是否应该避免使用完整类型名称作为通用最佳实践?
  3. 如何检查(可能使用反射),以确定加速器除了指向底层类之外还执行其他操作?

5
如果你反编译 System.Management.Automation.Language.Compiler.VisitConvertExpression,你会发现有三个类型名称有特殊处理:orderedPSCustomObjectref。请注意,本文仅为翻译内容,不涉及任何解释。 - user4003407
2个回答

7

看一下静态方法:

PS C:\> [PSCustomObject] | gm -Static -MemberType Method



   TypeName: System.Management.Automation.PSObject

Name            MemberType Definition                                                        
----            ---------- ----------                                                        
AsPSObject      Method     static psobject AsPSObject(System.Object obj)                     
Equals          Method     static bool Equals(System.Object objA, System.Object objB)        
new             Method     psobject new(), psobject new(System.Object obj)                   
ReferenceEquals Method     static bool ReferenceEquals(System.Object objA, System.Object o...



PS C:\> [System.Management.Automation.PSCustomObject] | gm -Static -MemberType Method



   TypeName: System.Management.Automation.PSCustomObject

Name            MemberType Definition                                                        
----            ---------- ----------                                                        
Equals          Method     static bool Equals(System.Object objA, System.Object objB)        
ReferenceEquals Method     static bool ReferenceEquals(System.Object objA, System.Object o...

类型加速器新增了几个静态方法。我猜测它正在使用其中一个作为构造函数。

4
[PSObject]和[PSCustomObject]是同一类型的别名——System.Management.Automation.PSObject。我不能说这个设计有什么好的原因,但至少表明了两种不同的目的,也许这就足够了。
System.Management.Automation.PSObject用于包装对象。它被引入是为了提供一个通用的反射API,以覆盖PowerShell包装的任何对象——.Net、WMI、COM、ADSI或简单的属性包。
System.Management.Automation.PSCustomObject只是一个实现细节。当你创建一个PSObject时,PSObject必须包装某些东西。对于属性包,被包装的对象是System.Management.Automation.PSCustomObject.SelfInstance(一个内部成员)。这个实例对于普通的PowerShell使用是隐藏的,唯一观察它的方式是通过反射。
在PowerShell中,可以通过多种方式创建属性包:
$o1 = [pscustomobject]@{Prop1 = 42}
$o2 = new-object psobject -Property @{Prop1 = 42 }

上述的$o1和$o2都将是PSObject的实例,而PSObject将包装PSCustomObject.SelfInstance。PSCustomObject.SelfInstance在PowerShell内部用于区分简单属性包和包装任何其他对象。


1
嗨Jason, Get-Member.GetType() 都将 $o1 和 $o2 识别为 PSCustomObject 的实例,而不是 PSObject。 - Burt_Harris
1
然而,如果我创建第三个对象 $o3 = [psobject]@{Prop1=42},那个对象是不同的。 - Burt_Harris
2
我的观点是上面的答案很令人困惑,因为在PowerShell中,[psobject][pscustomobject]的行为肯定不一样。 - Burt_Harris
1
是的,有点混淆,部分是我的错,部分是设计问题。我错过了调用GetType()来查看PSCustomObject的方式。在V3中,将[pscustomobject]强制转换为特殊处理的解析器,在此之前,它等同于强制转换为[psobject]。但归根结底 - 类型(和单例实例)System.Management.Automation.PSCustomObject是实现细节。如果操作数是哈希文字,则[pscustomobject]转换很有用,否则它等同于[psobject]转换。 - Jason Shirk
1
@JasonShirk 如果我没记错的话,使用[pscustomobject]转换(n v3或更高版本)在某些受限语言模式下可能会失败,而其他一些方法(New-ObjectSelect-Object?)在相同的上下文中可以正常工作。我现在不记得细节了,因为几年前我是通过(艰难的)实践发现的。 - briantist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接