为何Invoke-Command返回对象GetType与Get-Member显示类型不一致?
其实这俩命令看的是对象的不同层面,本质是PowerShell对反序列化远程对象的特殊处理导致的,咱们一步步说清楚:
1. 先搞懂Invoke-Command返回的是什么
当你用Invoke-Command远程执行命令时,返回的对象并不是原服务器上的System.ServiceProcess.ServiceController实例——因为跨进程/机器的对象不能直接传递,PowerShell会把对象序列化成XML(用PSRemoting的序列化格式),传到本地后再反序列化成一个“模拟”原对象的结构。
但这个反序列化出来的对象,并不是直接的原类型实例,而是被PowerShell用PSObject包装起来的容器,同时给它附加了原类型的元数据。
2. Get-Member看的是“逻辑类型”
Get-Member的工作逻辑是:它不会直接去查对象的底层CLR类型,而是优先读取对象的类型名称列表(也就是$obj.PSObject.TypeNames里的内容)。对于远程反序列化的对象,这个列表的第一个元素就是Deserialized.System.ServiceProcess.ServiceController——这是PowerShell给它加的“逻辑类型标识”,目的是让你直观知道这是从远程来的、对应原类型的反序列化对象,同时Get-Member会基于这个逻辑类型展示对应的属性和方法(虽然有些是模拟的,不是原类型的真实方法)。
你可以自己验证一下:
$remoteService = Invoke-Command -ScriptBlock { Get-Service } -ComputerName SERVER01 | Select-Object -First 1 $remoteService.PSObject.TypeNames
输出的第一个结果就是Deserialized.System.ServiceProcess.ServiceController,后面才会出现System.Management.Automation.PSObject。
3. GetType()看的是“底层CLR类型”
而GetType()是直接调用.NET CLR对象的原生方法,它只认最底层的实际类型。因为远程反序列化的对象本质上是被PSObject这个.NET类包装的,所以$_.GetType()返回的就是System.Management.Automation.PSObject——这是对象在CLR层面的真实类型。
总结一下
Get-Member展示的是PowerShell给对象赋予的逻辑类型,方便你理解它对应的原对象是什么,以及能使用哪些属性方法;GetType()展示的是对象在CLR层面的实际类型,也就是那个用来包装反序列化数据的PSObject容器。
这俩其实不矛盾,只是看问题的角度不同而已~
内容的提问来源于stack exchange,提问作者lit

