为什么PowerShell告诉我String不是String?并且只有在首先调用String.Length时

时间:2012-04-27 17:58:40

标签: powershell

我正在使用String设置XML属性,而PowerShell告诉我“只有字符串可以用作设置XmlNode属性的值”。这是一个简单的例子。首先,我运行这个:

$xmlDoc = [xml]@"
<root>
  <ComponentRef Id="a" />
</root>
"@

$newId = "b"

$length = $newId.Length

Write-Host ("`n`$newId is a string, see: `$newId.GetType().FullName = " + $newId.GetType().FullName + "`n")
Write-Host ("Running `"`$xmlDoc.root.ComponentRef.Id = `$newId`"...`n")
$xmlDoc.root.ComponentRef.Id = $newId
Write-Host ("ComponentRef.Id is now: " + $xmlDoc.root.ComponentRef.Id)

对我来说,输出是:

$newId is a string, see: $newId.GetType().FullName = System.String

Running "$xmlDoc.root.ComponentRef.Id = $newId"...

Cannot set "Id" because only strings can be used as values to set XmlNode properties.
At D:\Build\Tools\mass processing\Untitled4.ps1:14 char:27
+ $xmlDoc.root.ComponentRef. <<<< Id = $newId
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

ComponentRef.Id is now: a

该错误消息必定是错误的。等号右侧的值是一个字符串,如上面的输出所示。但它错了,所以XML属性仍然是“a”。现在它变得怪异了。让我们注释掉调用$ newId.length的行,并观察它是否正常工作。

评论如下:#$length = $newId.Length。输出现在是:

$newId is a string, see: $newId.GetType().FullName = System.String

Running "$xmlDoc.root.ComponentRef.Id = $newId"...

ComponentRef.Id is now: b

我不是要求修复,因为我知道如何通过转换到最后一个赋值运算符右侧的[string]来解决这个问题。我想知道的是:

任何人都可以解释为什么调用$ newId.Length(一个getter!)会导致PowerShell认为$ newId不再是一个字符串?

谢谢!

1 个答案:

答案 0 :(得分:6)

这是V2扩展类型系统中的一个不幸的错误,其中可以围绕基本.NET类型创建psobject包装器。向对象添加成员或访问不存在的属性时,可能会发生这种情况。当您访问对象IIRC上的psobject属性时也会发生这种情况,例如$newId.psobject。当你留在PowerShell中时,这通常不会导致任何问题。

更新:调用.NET不是问题。一些快速的.NET测试代码显示它为属性设置器获取了一个未包装的对象。使用Trace-Command查看此内容后,它似乎是PowerShell的XmlNodeAdapater中的错误:

DEBUG: ETS Information: 0 :  Method      Enter PSObject..ctor():object = System.Management.Automation.RuntimeException:
 Cannot set "Id" because only strings can be used as values to set XmlNode properties. --->
System.Management.Automation.SetValueException: Cannot set "Id" because only strings can be used as values to set
XmlNode properties.
   at System.Management.Automation.XmlNodeAdapter.PropertySet(PSProperty property, Object setValue, Boolean
convertIfPossible)
   at System.Management.Automation.Adapter.BasePropertySet(PSProperty property, Object setValue, Boolean convert)
   at System.Management.Automation.PSProperty.SetAdaptedValue(Object setValue, Boolean shouldConvert)
   at System.Management.Automation.PSProperty.set_Value(Object value)
   at System.Management.Automation.PropertyReferenceNode.SetValue(PSObject obj, Object property, Object value,
ExecutionContext context)
   --- End of inner exception stack trace ---
   at System.Management.Automation.PropertyReferenceNode.SetValue(PSObject obj, Object property, Object value,
ExecutionContext context)
   at System.Management.Automation.AssignablePropertyReference.SetValue(Object value, ExecutionContext context)
   at System.Management.Automation.AssignmentStatementNode.Execute(Array input, Pipe outputPipe, ExecutionContext
context)
   at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe
outputPipe, ArrayList& resultList, ExecutionContext context)

确保始终获得base .NET对象的一种方法是:

$xmlDoc.root.ComponentRef.Id = $newId.psobject.baseobject

好消息是这个问题在V3中得到修复,例如:

PS> $xmlDoc = [xml]@"
<root>
  <ComponentRef Id="a" />
</root>
"@

PS> $newId = "b"

PS> $newId.Length
1

PS> $newId.psobject.typenames
System.String
System.Object

PS> $xmlDoc.root.ComponentRef.Id = $newId

PS> $xmlDoc | format-xml # From PowerShell Community Extensions
<root>
  <ComponentRef Id="b" />
</root>