Проверка Powershell, является ли переменная объектом - PullRequest
2 голосов
/ 05 июля 2019

[Изменить после того, как оба ответа сказали, что нет ясности] Моя цель: выполнять различные операции в зависимости от того, передается ли функция функции типа «объект» (например, массив, хеш-таблица) или просто как элемент «строкового» типа. Если это просто строка, я просто включу ее в тело письма. Если это массив или хеш-таблица, мне нужно выполнить на нем кучу обработок, чтобы преобразовать его в таблицу 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() возвращает BaseType System.Object, однако я не могу ссылаться на свойство BaseType. ($hash.GetType()).BaseType возвращает сам другой объект, который сам имеет свойство BaseType, которое является пустым.

Я также пробовал $body.IsObject и $body.IsObject(), но эти методы, похоже, не существуют. Я также пробовал $body -eq [System.Object], который я ожидал равным $true, но он возвращает $false.

Не уверен, куда идти отсюда - мне кажется, я упускаю что-то очевидное или у меня логическая ошибка.

Ответы [ 2 ]

4 голосов
/ 05 июля 2019

Если вы просто хотите проверить тип объекта, вы можете использовать оператор -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
3 голосов
/ 05 июля 2019

Из вопроса о том, какова ваша цель или мотивация, не совсем понятно, но здесь говорится:

Каждый отдельный объект в PowerShell в конечном итоге наследуется от System.Object из-за характераСистема типов .NET, так что пытаться использовать сравнение идентификаторов типов - это немного глупо, поскольку вы можете просто сделать:

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 operator
  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, за исключением случаев, когда вы специально хотелитестировать только для базовых классов, но не для интерфейсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...