Powershell检查变量是否为对象

时间:2019-07-05 02:09:44

标签: powershell

[两个答案都说不清楚后再编辑] 我的目标:根据传递给函数的内容是“对象”类型的事物(例如数组,哈希表)还是简单的“字符串”类型的事物,执行不同的操作。如果只是一个字符串,我只需将其包含在电子邮件正文中。如果它是数组或哈希表,则需要对其进行大量处理,以将其转换为HTML表。

[原始问题文本] 我正在将变量$body传递给函数Email-Report,该函数可以是简单字符串或对象(例如,哈希表或数组)。 我想检查$body是否是一个对象,并根据情况做不同的事情。 我的问题是$body几乎可以是任何东西,而不仅仅是字符串或哈希表。所以我不能只检查$body.GetType().Name -eq String

我尝试过$body.GetType().Name会返回

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object

但是,如果变量是数组BaseType变为System.Array,那么我就无法对该属性进行过滤,同样如上所述,$body变量可能并不总是哈希表或数组。 如果是哈希表,$var.GetType()返回的System.Object的BaseType,但是我似乎无法引用BaseType属性。 ($hash.GetType()).BaseType返回另一个对象本身,该对象本身具有空白的BaseType属性。

我也尝试过$body.IsObject$body.IsObject(),但这些方法似乎不存在。我也尝试过使用$body -eq [System.Object],但预期会返回$true,但是它返回了$false

不确定从这里要去哪里-我想我缺少明显的东西或有逻辑错误。

2 个答案:

答案 0 :(得分:4)

如果只想测试对象的类型,则可以使用-is运算符将变量或值与类型进行比较。

# String test

PS > $str = "a string"
PS > $str -is [String]
True

PS > $str -is [Int]
False

PS > $str.gettype().Name
String

# Array Test

PS > $arr = @(1,2,3)
PS > $arr.GetType().Name
Object[]

PS > $arr -is [Object[]]
True

# Hashtable Test

PS > $hash = @{property='Value'}
PS > $hash.GetType().Name
Hashtable
PS > $hash -is [Hashtable]
True
PS > $hash -is [Object[]]
False
PS > $hash -is [String]
False

您分配的几乎所有变量将是一个对象或对该对象的引用。因此,将任何类型的测试[object]几乎总是True

为安全起见,在进行类型比较时应使用类型的全名,因为并非所有类型都具有类型加速器。以下面的ArrayList类型为例。类型名称可能是ArrayList,但是由于没有名为[ArrayList]的类型加速器,并且它不是直接从System名称空间中移出的,因此如果没有完整的类型名称,检查将引发错误。您可以随时退出系统,即[String][System.String]相同。

# Bad ArrayList Test

PS > $e = @(1,2) -as [Collections.ArrayList]
PS > $e.GetType().Name
ArrayList
PS > $e -is [ArrayList]
Unable to find type [ArrayList].
At line:1 char:8

# Good Arraylist Test

PS C:\temp\test1> $e.GetType().FullName
System.Collections.ArrayList
PS > $e -is [System.Collections.ArrayList]
True

答案 1 :(得分:3)

问题不完全清楚您的目标或动机在哪里,但这里有:

由于.NET类型系统的性质,PowerShell中的每个单个对象最终都最终继承自System.Object,因此尝试对其进行类型标识比较是一个愚蠢的测试,因为您可以简单地做到以下几点: / p>

function Test-IsObject
{
    param(
        [AllowNull()]
        $InputObject
    )

    return $null -ne $InputObject
}

如果要测试有关对象不是不是值类型(即不是结构或整数类型,而是类),请检查IsValueType属性类型:

function Test-IsRefType
{
    param(
        [AllowNull()]
        $InputObject
    )

    return ($null -ne $InputObject -and -not $InputObject.GetType().IsValueType)
}

如果要使用通用解决方案来测试对象的类型层次结构中是否出现某种类型,可以使用以下三种通用方法:

  1. 依靠PSTypeName
  2. 使用-is运算符
  3. 自己解决所有基本类型

PSTypeName

PowerShell中的所有对象都有一个名为PSTypeName的特殊属性,其中包含基础对象的类型层次结构中所有类型的所有类型的非限定类型名称+(可选)PowerShell定义的类型扩展名-这是PowerShell区分的方式例如,不同CIM类的实例的格式。

由于PSTypeName可以由用户直接操作,因此这本质上是“不安全的”,但是在大多数情况下仍然可以使用:

function Test-IsType
{
    param(
        [object]$InputObject,
        [string]$TypeName
    )

    return $InputObject -contains $TypeName 
}

建筑物类型运算符

自PowerShell 3.0以来,我们有两个新的类型运算符:-is及其否定的-isnot。这些实际上检查基础.NET对象的运行时类型,因此,它们比检查综合PSTypeName属性更安全:

$Object -is [System.Object]
"" -is [string]
5 -is [int]

-is也会自动测试基本类型和接口:

$strings = 'a', 'b', 'c' -as [string[]]
$strings -is [array]
$strings -is [System.Collections.Generic.IEnumerable[string]]
$strings -is [object]

这些运算符以及相关的-as运算符,都记录在about_Type_Operators帮助主题中。

手动解决类型层次结构

最后,如果我们想进一步探索,可以通过简单地取消引用GetType().BaseType直到我们点击System.Object来手动解决类型层次结构。下面是一个简单的辅助函数,它发出所有基本类型,然后可以与之进行比较:

function Get-BaseType
{
    param(
        [type]$Type,
        [switch]$IncludeLeaf
    )

    if($IncludeLeaf){
        # We're "walking backwards", so we'll start by emitting the type itself
        $Type
    }

    # Now go through the BaseType references
    # At some point we'll reach System.Object and (BaseType -eq $null) 
    while($BaseType = $Type.BaseType){
        ($Type = $BaseType)
    }
}

function Test-IsType
{
    param(
        [object]$InputObject,
        [type]$TypeName
    )

    return $TypeName -in (Get-BaseType -Type $InputObject.GetType() -IncludeLeaf)
}

请注意,您可以只使用-is代替Test-IsType,除非您特别想只测试基类,而不要测试接口。