Пользовательский хост PowerShell и преобразование PSObject обратно в базовый тип - PullRequest
12 голосов
/ 25 февраля 2010

При размещении среды выполнения PowerShell возможно ли преобразовать PSObject обратно в исходный тип, как?

Например:

У меня есть командлет, который вызывает WriteObject и отправляет коллекцию ClassXzy в конвейер. Когда я вызываю PowerShell.Invoke с хоста, я получаю коллекцию PSObject s со свойством BaseObject. Приведение BaseObject к ClassXyz завершается неудачно.

Есть ли способ сопоставить каждое значение свойства с соответствующим исходным объектом?
Я предполагаю, что PowerShell делает это каким-то образом, поскольку вы можете передавать PSObject s командлетам, и они переводятся в типы параметров. Но как?

Я потратил много времени на разрывы PS-сборок с Reflector, но на самом деле не придал значения тому, как происходит это волшебство.

Есть идеи?

РЕДАКТИРОВАТЬ: я забыл одну очень важную деталь. PSObject, который я проверяю, является удаленным объектом, поэтому тип BaseObject называется Deserialized.ClassXyz. Вот почему я вижу такое странное поведение.

Ответы [ 3 ]

10 голосов
/ 25 февраля 2010

Кит ответил на ваш вопрос, прежде чем вы упомянули процесс десериализации.

Что касается сериализации / десериализации

Я сомневаюсь, что возможно получить оригинальный объект. Я не знаю, какой тип сериализации использует PowerShell, но если рассмотреть простую сериализацию Xml, то вы можете понять, что можете сериализовать только свойства и ничего больше.
Вы не можете сериализовать тела его методов.
Вы не можете сериализовать всех подписчиков этого события (или, возможно, в некоторых случаях это было бы возможно, но я не такой специалист по .NET).
А поскольку тип (как в моем примере) может быть недоступен (например, сборка присутствует только на удаленном компьютере), необходимо будет передать всю информацию о типе.

Речь идет не только о типе, но и обо всей иерархии наследования и интерфейсах, которые реализует объект. Они бы тоже как-то сериализовались.

Просто попробуйте этот пример:

$deserialized = Start-Job {
    Add-Type -TypeDefinition @"
    public class Parent {
        public override string ToString() { return "overriden parent"; }
        public int IntParent { get { return 1; } }
    }
    public class TestClass : Parent
    {
        public string GString() { return "this is a test string"; }
        public override string ToString() { return "overriden tostring" + System.DateTime.Now.ToString(); }
        public int IntProp { get { return 3451; } }
    }
"@
    New-Object TestClass
} | Wait-Job | Receive-Job
$deserialized.ToString()
$deserialized | gm -for

Вы увидите, что PowerShell

  • выравнивает иерархию наследования.
  • «реализует» только свойства
  • и поскольку ему известно значение ToString(), он также может добавить результат метода. Но, как вы можете видеть, информация, возвращаемая с ToString(), больше не отражает изменение даты - это замороженное значение.

Я не вижу никакой разницы между сериализацией для удаленного взаимодействия, сериализацией в clixml (через Export-CliXml) или когда Receive-Job при рассмотрении того, что я написал выше, поэтому я думаю, что в обоих случаях это невозможно.

3 голосов
/ 25 февраля 2010

Вы можете получить доступ либо к свойству BaseObject в PSObject (которое обходит каждый объект PSO, пока не достигнет реального базового объекта), либо к ImmediateBaseObject, который просто захватывает следующий объект в цепочке.

0 голосов
/ 23 декабря 2011

Вы можете сделать то, что описано выше, однако все эти методы не будут работать после того, как PSRemoting вступит в игру, поскольку вы будете получать доступ к прокси-объекту. Лучше всего использовать PSMembers & PSProperties

...