【问题标题】:Difference between PSObject, Hashtable, and PSCustomObjectPSObject、Hashtable 和 PSCustomObject 之间的区别
【发布时间】:2012-12-10 08:52:30
【问题描述】:

谁能解释一下细节?如果我使用

创建一个对象
$var = [PSObject]@{a=1;b=2;c=3}

然后我使用getType()查找它的类型,PowerShell 告诉我它的类型是 Hashtable

当使用Get-Member(别名gm)检查对象时,很明显已经创建了一个哈希表,因为它有一个keys 和一个values 属性。那么与“普通”哈希表有什么区别?

另外,使用 PSCustomObject 有什么好处?当使用这样的东西创建一个时

$var = [PSCustomObject]@{a=1;b=2;c=3}

对我来说唯一明显的区别是 PSCustomObject 的不同数据类型。同样,使用 gm 进行检查的不是键和值属性,而是显示现在每个键都已添加为 NoteProperty 对象。

但是我有什么优势呢?我可以通过使用它的键来访问我的值,就像在哈希表中一样。我可以在 PSCustomObject 中存储多个简单的键值对(例如键对象对),就像在哈希表中一样。那么有什么好处呢?有什么重要的区别吗?

【问题讨论】:

    标签: powershell powershell-3.0 psobject


    【解决方案1】:

    我认为您会看到的最大差异是性能。看看这篇博文:

    Combining Objects Efficiently – Use a Hash Table to Index a Collection of Objects

    作者运行如下代码:

    $numberofobjects = 1000
    
    $objects = (0..$numberofobjects) |% {
        New-Object psobject -Property @{'Name'="object$_";'Path'="Path$_"}
    }
    $lookupobjects = (0..$numberofobjects) | % {
        New-Object psobject -Property @{'Path'="Path$_";'Share'="Share$_"}
    }
    
    $method1 = {
        foreach ($object in $objects) {
            $object | Add-Member NoteProperty -Name Share -Value ($lookupobjects | ?{$_.Path -eq $object.Path} | select -First 1 -ExpandProperty share)
        }
    }
    Measure-Command $method1 | select totalseconds
    
    $objects = (0..$numberofobjects) | % {
        New-Object psobject -Property @{'Name'="object$_";'Path'="Path$_"}
    }
    $lookupobjects = (0..$numberofobjects) | % {
        New-Object psobject -Property @{'Path'="Path$_";'Share'="Share$_"}
    }
    
    $method2 = {
        $hash = @{}
        foreach ($obj in $lookupobjects) {
            $hash.($obj.Path) = $obj.share
        }
        foreach ($object in $objects) {
            $object |Add-Member NoteProperty -Name Share -Value ($hash.($object.path)).share
        }
    }
    Measure-Command $method2 | select totalseconds
    

    博客作者的输出:

    TotalSeconds
    ------------
     167.8825285
       0.7459279
    

    他对代码结果的评论是:

    当你把它们放在一起时,你可以看到速度的差异。对象方法在我的计算机上需要 167 秒,而哈希表方法需要不到一秒的时间来构建哈希表然后进行查找。

    以下是其他一些更微妙的好处: Custom objects default display in PowerShell 3.0

    【讨论】:

    • 嗯,性能 - 好的。但这就是一切吗?性能也是专业哈希表。那么 PSCustomObject 的存在权是什么?而默认显示选项,真的吗?我们通常在处理数据——没有人愿意每天都显示这些东西。有时,当我需要显示一些数据时,我仍然可能会构建一个小循环并使用标准格式化程序。对我来说似乎不足以证明新数据类型的合理性。
    • 第一个链接中的文章没有提到 psobject 与 pscustomobject 的速度。相反,它强调了 O(mn) 和 O(mln n) 算法之间的区别。
    • "始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。" - stackoverflow.com/help/how-to-answer
    • 有趣的是,我只运行了脚本,在本地只运行了 10 次,时差远没有那么大。我假设这个用户版本的 Powershell 和我的(5.1 版)或其他因素(包括机器负载)之间有一些优化。无论如何,方法 1 的结果始终在 7-8 秒左右,方法 2 的结果仅超过 1.5 秒。
    • 上面的代码除了管道很慢之外没有任何证明。将此代码用作 method1 并且它比另一个更快: $method1 = { foreach ($object in $objects) { $value = foreach($lookup in $lookupobjects) { if ($lookup.Path -eq $object.Path) { $lookup.share;打破 } } $对象 | Add-Member NoteProperty -Name Share -Value $value } }
    【解决方案2】:

    我认为 PSObject 的一个优点是您可以使用它创建自定义方法。

    例如,

    $o = New-Object PSObject -Property @{
       "value"=9
    }
    Add-Member -MemberType ScriptMethod -Name "Sqrt" -Value {
        echo "the square root of $($this.value) is $([Math]::Round([Math]::Sqrt($this.value),2))"
    } -inputObject $o
    
    $o.Sqrt()
    

    您可以使用它来控制 PSObject 属性的排序顺序(参见 PSObject sorting

    【讨论】:

    • 请注意,您可以同时使用 PSObject 或 PSCustomObject。
    【解决方案3】:

    使用[PSCustomObject] 而不是HashTable 的一种情况是您需要它们的集合。以下是为了说明它们在处理方式上的区别:

    $Hash = 1..10 | %{ @{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
    $Custom = 1..10 | %{[PSCustomObject] @{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
    
    $Hash   | Format-Table -AutoSize
    $Custom | Format-Table -AutoSize
    
    $Hash   | Export-Csv .\Hash.csv -NoTypeInformation
    $Custom | Export-Csv .\CustomObject.csv -NoTypeInformation
    

    Format-Table 将为$Hash 产生以下结果:

    Name    Value
    ----    -----
    Name    Object 1
    Squared 1
    Index   1
    Name    Object 2
    Squared 4
    Index   2
    Name    Object 3
    Squared 9
    ...
    

    以下是$CustomObject

    Name      Index Squared
    ----      ----- -------
    Object 1      1       1
    Object 2      2       4
    Object 3      3       9
    Object 4      4      16
    Object 5      5      25
    ...
    

    Export-Csv 也会发生同样的事情,因此使用 [PSCustomObject] 而不是简单的 HashTable 的原因。

    【讨论】:

      【解决方案4】:

      假设我想创建一个文件夹。如果我使用 PSObject,您可以通过以下方式判断它是错误的 看着它

      PS > [PSObject] @{Path='foo'; Type='directory'}
      
      Name                           Value
      ----                           -----
      Path                           foo
      Type                           directory
      

      但是 PSCustomObject 看起来是正确的

      PS > [PSCustomObject] @{Path='foo'; Type='directory'}
      
      Path                                    Type
      ----                                    ----
      foo                                     directory
      

      然后我可以管道对象

      [PSCustomObject] @{Path='foo'; Type='directory'} | New-Item
      

      【讨论】:

      • 赞成投票,因为这正是我正在寻找的答案。
      • 这里也一样。 hashtable 给了我“名称,值”,这让我很难使用。使用PSCustomObject 让我可以正常使用管道。
      • @Doug & @Tyler - 我知道这些 cmets 有点旧,但“正常管道”不一定是我所说的给定示例。如果您已经有一个可以通过管道传输到New-Item 的对象;伟大的。如果不是,我认为hashtable 会“更正常”,但没有管道。请参阅about_Splatting 了解更多信息,基本上是$hashtable=@{Path='foo'; Type='directory'} 然后New-Item @hashtable
      【解决方案5】:

      From the PSObject documentation:

      包装一个对象,提供可用成员的替代视图以及扩展它们的方法。成员可以是方法、属性、参数化属性等。

      换句话说,PSObject 是一个对象,您可以在创建它之后为其添加方法和属性。

      From the "About Hash Tables" documentation:

      哈希表,也称为字典或关联数组,是一种紧凑的数据结构,用于存储一个或多个键/值对。

      ...

      哈希表经常被使用,因为它们对于查找和检索数据非常有效。

      您可以像使用Hashtable 一样使用PSObject,因为PowerShell 允许您向PSObjects 添加属性,但您不应该这样做,因为您将无法访问Hashtable 特定功能,例如KeysValues 属性。此外,可能会产生性能成本和额外的内存使用量。

      The PowerShell documentation has the following information about PSCustomObject:

      当使用 PSObject 的无参数构造函数时,用作占位符 BaseObject。

      这对我来说并不清楚,但来自the co-author of a number of PowerShell booksa post on a PowerShell forum 似乎更清楚:

      [PSCustomObject] 是一个类型加速器。它构造了一个 PSObject,但这样做的方式是导致哈希表键成为属性。 PSCustomObject 本身不是一个对象类型——它是一个进程快捷方式。 ... PSCustomObject 是在没有构造函数参数的情况下调用 PSObject 时使用的占位符。

      关于您的代码,@{a=1;b=2;c=3}Hashtable[PSObject]@{a=1;b=2;c=3} 不会将 Hashtable 转换为 PSObject 或生成错误。该对象仍然是Hashtable。但是,[PSCustomObject]@{a=1;b=2;c=3}Hashtable 转换为PSObject。我找不到说明为什么会发生这种情况的文档。

      如果您想将 Hashtable 转换为对象以便将其键用作属性名称,您可以使用以下代码行之一:

      [PSCustomObject]@{a=1;b=2;c=3}
      
      # OR
      
      New-Object PSObject -Property @{a=1;b=2;c=3}
      
      # NOTE: Both have the type PSCustomObject
      

      如果您想将多个Hashtables 转换为一个对象,其中它们的键是属性名称,您可以使用以下代码:

      @{name='a';num=1},@{name='b';num=2} |
       % { [PSCustomObject]$_ }
      
      # OR
      
      @{name='a';num=1},@{name='b';num=2} |
       % { New-Object PSObject -Property $_ }
      
      <#
      Outputs:
      
      name num
      ---- ---
      a      1
      b      2
      #>
      

      很难找到有关 NoteProperty 的文档。 In the Add-Member documentation,没有任何-MemberType 对添加NoteProperty 以外的对象属性有意义。 Windows PowerShell Cookbook(第 3 版)将 Noteproperty Membertype 定义为:

      由您提供的初始值定义的属性

      • 李 H. (2013)。 Windows PowerShell 说明书。 O'Reilly Media, Inc. p。 895.

      【讨论】:

      • 赞成这一点,因为这似乎更详细地说明了每个与支持参考材料之间的差异。
      • 好东西。请注意,[PSCustomObject]@{a=1;b=2;c=3}直接 构造自定义对象的 语法糖,而不是通过中间哈希表(尽管语法暗示);如果涉及中间哈希表,则无法保证属性的顺序,但确实如此。请注意,类型加速器 [pscustomobject][psobject] 指的是 same 类型 System.Management.Automation.PSObject。但是,“纯”PSObject(具有 ETS 属性(不包装 .NET 对象)的自定义对象)将其类型报告为System.Management.Automation.PSCustomObject
      【解决方案6】:

      我们的 Windows-PKI 中有一堆模板,我们需要一个脚本,它必须与所有活动模板一起工作。我们不需要动态添加或删除模板。 对我来说完美的作品(因为它读起来也很“自然”)如下:

      $templates = @(
          [PSCustomObject]@{Name = 'template1'; Oid = '1.1.1.1.1'}
          [PSCustomObject]@{Name = 'template2'; Oid = '2.2.2.2.2'}
          [PSCustomObject]@{Name = 'template3'; Oid = '3.3.3.3.3'}
          [PSCustomObject]@{Name = 'template4'; Oid = '4.4.4.4.4'}
          [PSCustomObject]@{Name = 'template5'; Oid = '5.5.5.5.5'}
      )
      
      foreach ($template in $templates)
      {
         Write-Output $template.Name $template.Oid
      }
      

      【讨论】:

        猜你喜欢
        • 2016-06-27
        • 2010-11-15
        • 2020-10-11
        • 2021-02-25
        • 2012-02-11
        • 2016-06-23
        • 2012-01-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多