Пользовательский PowerShell Host и преобразование PSObject обратно в базовый тип

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

Например:

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

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

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

Любые идеи?

EDIT: Я забыл одну очень важную деталь. PSObject, который я тестирую, является удаленным объектом, поэтому тип BaseObject имеет имя Deserialized.ClassXyz. Вот почему я вижу такое странное поведение.

Ответ 1

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

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

Я сомневаюсь, что можно получить оригинальный объект. Я не знаю, какой тип сериализации использует PowerShell, но если вы рассматриваете простую сериализацию Xml, тогда вы можете понять, что вы можете сериализовать только свойства и ничего больше.
Вы не можете сериализовать bodys своих методов.
Вы не можете сериализовать всех подписчиков событий (или, возможно, в некоторых случаях это было бы возможно, но я не такой эксперт .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 при рассмотрении того, что я написал выше, поэтому я думаю, что в обоих случаях это невозможно.

Ответ 2

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

Ответ 3

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