为什么 PowerShell 告诉我字符串不是字符串?并且只有在调用 String.Length 时,首先 [英] Why is PowerShell telling me a String is not a String? And only when calling String.Length, first

查看:59
本文介绍了为什么 PowerShell 告诉我字符串不是字符串?并且只有在调用 String.Length 时,首先的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm setting an XML attribute with a String, and PowerShell tells me "only strings can be used as values to set XmlNode properties". Here's a simple example. First, I run this:

$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 的行,并观察它是否正常工作.

That error message has got to be wrong. The value on the right side of the equals sign is a String, as shown in the output above. But it errored, so the XML attribute still reads "a". Now it gets weirder. Let's comment out the line that calls $newId.length, and watch it work correctly.

注释如下:#$length = $newId.Length.现在的输出是:

Commenting out as such: #$length = $newId.Length. The output is now:

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

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

ComponentRef.Id is now: b

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

I'm not asking for a fix, because I know how to work around this issue by casting to [string] on the right side of the last assignment operator. What I'd like to know is:

谁能解释为什么调用 $newId.Length(一个 getter!)会导致 PowerShell 认为 $newId 不再是一个字符串?

Can anyone explain why calling $newId.Length (a getter!) could cause PowerShell to think $newId is no longer a string?

谢谢!

推荐答案

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

This is an unfortunate bug in the V2 extended type system where a psobject wrapper can get created around a base .NET Type. This can happen when you add members to an object or when you access a property that doesn't exist. It can also happen when you access the psobject property on an object IIRC e.g. $newId.psobject. When you stay within PowerShell this usually doesn't cause any problems.

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

Update: This isn't a problem calling out to .NET. Some quick .NET test code shows that it gets an unwrapped object for a property setter. After looking at this with Trace-Command it appears to be a bug in PowerShell's 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 对象的一种方法是:

One way to ensure you always get the base .NET object is:

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

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

Good news is that this issue is fixed in V3 e.g.:

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>

这篇关于为什么 PowerShell 告诉我字符串不是字符串?并且只有在调用 String.Length 时,首先的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆