1

我正在尝试使用自定义 types.ps1xml 文件扩展自定义对象的类型。该文件以我想要的方式工作并扩展了许多类型。

我已经能够使用布尔值为 $true 的 NoteProperty 扩展自定义类型。我现在想要做的就是将 NoteProperty 添加到另一个类型,其 TypeName 值为 System.Boolean,值为 $false。我的问题是该<value>属性始终被解释为字符串,并且当转换为 System.Boolean 的 TypeName 时,它​​总是返回 $true,因为任何非零长度字符串都返回 $true。

我正在手机上输入这个并急于回去工作,但 ps1xml 文件的相关部分是:

<Type>
  <Name>My.Custom.Object</Name>
  <Members>
    <NoteProperty>
      <Name>PSIsContainer</Name>
      <TypeName>System.Boolean<TypeName>
      <Value>True</Value>
    </NoteProperty>
  </Members>
</Type>

这会正确生成反映布尔值 $true 的 NoteProperty。但是,如果我将值更改为 false、或 0、或 null、或 ''、或 ""、或 $false、或 [bool] 0,或任何其他带或不带引号或带或不带 $ 或内部的可行值$() 或带有转义的引号或字符;它总是以 $true 的布尔值返回。如果我<Value>留空,它会在加载 xml 时给我一个错误,所以这也不起作用。如果我将 更改<TypeName>为其他内容,例如 System.IO.File 或其他任何内容,它实际上会将“false”的值转换为名为“false”的文件对象,或者我在<TypeName>. 很明显它按预期工作,除了我不能分配 $false 的布尔值。不管我做什么。如果我离开<TypeName>完全没有它默认为 System.String ,这对我没有帮助。

如果您查看文件或目录的 Get-Member,您将看到 PIsContainer 的相应 NoteProperty 等于 $true 或 $false 的 System.Boolean 值,具体取决于;当设置为 true 时,我的输出与默认目录对象完全相同。这表明应该可以将 NoteProperty 设置为 $false。

我差点忘了,但事实上,我可以使用 Add-Member 添加一个 NoteProperty 并将值正确设置为 $false,但是为了让我的脚本/工具正常工作,我需要这个对象的所有实例都具有这个 NoteProperty 等等使用 types.ps1xml 文件是我能想到的唯一方法。

请注意,我设置此属性的对象是一个自定义对象,它没有任何与该名称或值冲突或现有的属性。<Name>如果我把它改成别的东西也没关系,比如“IsAwesome”;它仍然不适用于 $false。我尝试在现有的 types.ps1xml 和 format.ps1xml 中搜索,但没有提示他们是如何做到的。我已经在网上广泛搜索了几天,甚至查看了一些关于 Powershell 的教科书,但无济于事。

有人可以帮帮我吗!?

4

1 回答 1

2

我不认为这是可能的。我使用ILSpy来查看cmdlet 所在的Microsoft.PowerShell.Commands.Utility程序集。UpdateTypeDataCommand在我看来,它使用ProcessNote方法 fromRunspaces.TypeTable添加注释属性。这是它的相关部分:

// System.Management.Automation.Runspaces.TypeTable
private static void ProcessNote(
    LoadContext context,
    string typeName,
    Node node,
    PSMemberInfoInternalCollection<PSMemberInfo> membersCollection,
    Collection<int> nodeLineNumbers
)
{

...

if (actualNodes.Count == 1)
{
    if (actualNodes[0].nodeError)
    {
        return;
    }
    Type typeFromString = TypeTable.GetTypeFromString(
        context,
        typeName,
        actualNodes[0]
    );
    if (typeFromString == null)
    {
        return;
    }
    try
    {
        obj = LanguagePrimitives.ConvertTo(
            obj,
            typeFromString,
            CultureInfo.InvariantCulture
        );
    }
    catch (PSInvalidCastException ex)
    {
        context.AddError(typeName,
            actualNodes[0].lineNumber,
            TypesXmlStrings.Exception,
            new object[] {ex.Message}
        );
        return;
    }
}

PSNoteProperty pSNoteProperty = new PSNoteProperty(node2.innerText, obj);
pSNoteProperty.isHidden = (node.isHidden.HasValue && node.isHidden.Value);
TypeTable.AddMember(context,
    typeName,
    node.lineNumber,
    pSNoteProperty,
    membersCollection, 
    nodeLineNumbers
);

如果您<TypeName>在 XML 中指定,它会尝试将 NoteProperty 的值转换为它。而且由于原始值是一个字符串,因此看起来像调用了这个方法:

// System.Management.Automation.LanguagePrimitives
private static bool ConvertStringToBool(
    object valueToConvert,
    Type resultType,
    bool recursion,
    PSObject originalValueToConvert,
    IFormatProvider formatProvider,
    TypeTable backupTable
)
{
    LanguagePrimitives.typeConversion.WriteLine(
        "Converting string to boolean.",
        new object[0]
    );
    return LanguagePrimitives.IsTrue((string)valueToConvert);
}

可悲的是,LanguagePrimitives.IsTrue方法的主体很简单:

// System.Management.Automation.LanguagePrimitives
internal static bool IsTrue(string s)
{
    return s.Length != 0;
}

如果字符串不为空,则将其转换为true. 并且由于 XML ( ) 中不能有空<Value>节点,Update-TypeData : Error: The node Value is not allowed.这实际上使得无法创建具有false布尔值的 NoteProperty。

PS虽然它对应于观察到的行为,但它仍然主要是猜测,所以如果我错了,请随时纠正我。

于 2015-12-29T15:36:41.623 回答