【问题标题】:PowerShell conversion of String into FileInfo automatically - What is this called?PowerShell 自动将 String 转换为 FileInfo - 这叫什么?
【发布时间】:2022-02-02 16:47:26
【问题描述】:

一些示例代码来说明我在说什么。我有一个具有单一方法的实用程序类。该方法采用一个参数:System.IO.FileInfo 对象。从 PowerShell 方面,我可以传入一个 System.String 对象,一切都“正常工作”。我很好奇,今年我对 PowerShell 的开发越来越多,我很好奇:

  1. PowerShell 的哪些功能允许这种情况发生?
  2. 这是否对任何开发人员在 PowerShell 7.x 中的扩展/使用开放? (非内部)

C#:

using System.IO;
using System.Linq;

namespace TestProject
{
    public class Utility
    {
        public string GetFirstLine(FileInfo fileInfo)
        {
            string firstLine = File.ReadLines(fileInfo.FullName).First();
            return firstLine;
        }
    }
}

PowerShell:

Add-Type -Path "C:\assemblies\TestProject.dll"
$util = [TestProject.Utility]::New()
$util.GetFirstLine("C:\temp\random-log.txt")

注意:我意识到这是一个微不足道的例子。代码旨在快速说明我感兴趣的 PowerShell 功能。

【问题讨论】:

  • PowerShell 如果可能会自动转换;就好像你写了$util.GetFirstLine(([IO.FileInfo] "C:\temp\random-log.txt"))
  • 功能是指尽可能进行类型转换的能力?
  • 这只是隐式转换 - PowerShell 尝试解析正确的方法签名,找不到完全匹配,然后尝试将字符串值转换为目标类型。如果为目标类型注册了类型转换器或类型适配器,则使用它,否则 PowerShell 解析目标类型的构造函数,该构造函数接受源类型的 1 个参数,并使用实例化此类对象的结果(例如 GetFirstLine("C:\temp\random-log.txt")在运行时被评估为GetFirstLine([System.IO.FileInfo]::new("C:\temp\random-log.txt"))
  • @BuddyJoe 我正在使用我的手机 rn,我会在回到全键盘后立即发布答案 :)
  • 很高兴知道有官方文档,@BuddyJoe - 尽管链接的主题非常技术性。可以在this answer 中找到关于 PowerShell 自动类型转换/强制转换的更容易理解的摘要。

标签: c# powershell types type-conversion powershell-7.0


【解决方案1】:

尽管 PowerShell 或多或少是一种“真正的 .NET 语言”,但它确实扩展了具有一系列行为的通用类型系统,这些行为有时与您对 .NET 类型系统行为的了解相冲突像 C# 或 VB.NET 这样的语言。

这个附加在 CLR 之上的类型系统层被恰当地命名为 Extended Type System (ETS),它直接负责使参数转换按照您观察到的方式“正常工作” .


当 PowerShell 运行时到达像 $util.GetFirstLine("C:\temp\random-log.txt") 这样的方法调用语句时,它必须像 C# 编译器一样选择适当的重载。

第一步是识别重载,为其传递的参数数量可以覆盖给定重载签名的所有强制参数。在您的情况下,此步骤只能解决 1 个这样的签名,因此 PowerShell 现在有两个难题:

  • 带有签名string GetFirstLine(FileInfo fileInfo) 的方法存根
  • [string] 类型的参数值

此时,C#编译器会放弃并报告CS1503: Argument 1: cannot convert from 'string' to 'System.IO.FileInfo'

另一方面,PowerShell 的设计目的是帮助系统管理员和操作员——他们可能不会过多考虑数据结构设计,但主要关心的是数据的字符串表示(毕竟这是bashcmd 等教他们使用的)。

为了在正确的时间从字符串(或任何其他不兼容的参数类型)进行有意义的转换,ETS 附带了a number of facilities and hooks for implicit type conversion,然后运行时会一一尝试,直到找到有效的转换机制,或失败(此时方法调用也失败):

  • 内置转换器

    • 这些优先处理最常见的边缘情况,例如null-conversions、“truthy/falsy”-to-real-[bool] 转换、标量的字符串化等 - 如果没有这些,PowerShell 将是一个以交互方式工作很痛苦。
    • 示例:
      • 这些流控制语句的行为与您期望的完全一样,这要归功于内置的转换:if($null){ <# unreachable #> }do{ Stop-Process chrome }while(Get-Process chome)
  • 自定义类型转换器

    • 这些是注册到一种或多种目标类型的具体类型转换器实现 - 这对于修改您带入运行时的复杂类型的绑定行为很有用。
    • 示例:
      • RSAT ActiveDirectory 模块使用类型适配器在建模特定目录对象类的不同数据类型之间进行交换 - 允许您将输出从 Get-ADComputer(非常具体的输出类型)无缝管道传输到 Get-ADObject(通用输出类型),反之亦然反之亦然。
  • 解析转换器

    • 如果找不到合适的内置或自定义类型转换器,PowerShell 将尝试使用目标类型上的适当返回类型解析 Parse() 方法。
    • 示例:
      • 演员操作[timespan]'1.02:15:25'可以这样成功。
  • 基于构造函数的转换

    • 如果上述方法均无效,PowerShell 将尝试解析一个构造函数,该构造函数可以使用给定源类型的单个参数参数调用。
    • 这就是你的情况 - PowerShell 有效地为你执行 $util.GetFirstLine([System.IO.FileInfo]::new("C:\temp\random-log.txt"))
  • CTS 转化次数

    • 最后,如果 ETS 的所有对话尝试都失败,它会回退到解决由类型本身定义的隐式(最终显式)转换。
    • 示例:
      • 此转换成功,因为[DateTimeOffset] 类型具有[DateTime] 的隐式转换运算符:(Get-Date) -as [DateTimeOffset]

如果上面提到的具体类型转换器被包含在类型数据文件中(参见about_Types.ps1xml),或者如果目标类型是公共的并且源定义被装饰了@ 987654342@属性。

此外,您可以在运行时使用Update-TypeData cmdlet 注册新的类型转换原语。

当然,疯狂并不止于此,还有专门用于转换/转换 command 参数的额外工具,但这超出了本问题的范围 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 2013-03-25
    相关资源
    最近更新 更多