PowerShell 自动将 String 转换为 FileInfo - 这叫什么?
PowerShell conversion of String into FileInfo automatically - What is this called?
一些示例代码来说明我在说什么。我有一个使用单一方法的实用程序 class。该方法采用一个参数:一个 System.IO.FileInfo 对象。从 PowerShell 方面,我可以传入一个 System.String 对象,一切都“正常工作”。我很好奇作为开发人员今年越来越多地使用 PowerShell 我很好奇:
- PowerShell 的什么功能允许这种情况发生?
- 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 或多或少是一种“真正的 .NET 语言”,但它确实扩展 具有一系列有时与您所了解的行为相冲突的行为的通用类型系统。 NET 的类型系统行为来自 C# 或 VB.NET.
等语言
这个附加在 CLR 之上的类型系统层被恰当地命名为 扩展类型系统 (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 旨在为系统管理员和操作员提供帮助 - 他们可能不会过多考虑数据结构设计,但他们主要关注数据的字符串表示(毕竟,这是bash
、cmd
等教他们使用的东西)。
为了在正确的时间从字符串(或任何其他 non-compliant 参数类型)进行有意义的转换,ETS 附带了 a number of facilities and hooks for implicit type conversion,然后运行时会逐一尝试,直到它要么找到有效的转换机制,或失败(此时方法调用也失败):
Built-in 转换器
- 这些优先处理最常见的边缘情况,例如
null
-转换,“truthy/falsy”-to-real-[bool]
转换,标量的字符串化,等等 - 如果没有这些,PowerShell 将是一个交互工作的麻烦。
- 示例:
- 由于 built-in 转换,这些流程控制语句的行为完全符合您的预期:
if($null){ <# unreachable #> }
、do{ Stop-Process chrome }while(Get-Process chome)
自定义类型转换器
- 这些是注册到一个或多个目标类型的具体类型转换器实现 - 这对于修改您带入运行时的复杂类型的绑定行为很有用。
- 示例:
- RSAT ActiveDirectory 模块使用类型适配器在建模特定目录对象的不同数据类型之间进行交换 类 - 允许您将输出从
Get-ADComputer
(非常具体的输出类型)无缝传输到 Get-ADObject
(通用输出类型),反之亦然。
解析转换器
- 如果找不到合适的 built-in 或自定义类型转换器,PowerShell 将尝试在目标类型上使用合适的 return 类型解析
Parse()
方法。
- 示例:
- 转换操作
[timespan]'1.02:15:25'
可以这样成功。
Constructor-based 次转化
- 如果上述 none 有效,PowerShell 将尝试解析可以使用给定源类型的单个参数调用的构造函数。
- 这就是您的情况 - PowerShell 有效地为您执行
$util.GetFirstLine([System.IO.FileInfo]::new("C:\temp\random-log.txt"))
CTS 转化次数
- 最后,如果 ETS 的所有对话尝试都失败了,它会回退到解决由类型本身定义的隐式(最终是显式)转换。
- 示例:
- 此转换成功,因为
[DateTimeOffset]
类型具有 [DateTime]
的隐式转换运算符:(Get-Date) -as [DateTimeOffset]
如果上述具体类型转换器已包含在类型数据文件中(参见 about_Types.ps1xml
),或者如果目标类型为 public 并且源定义用 [TypeConverter()]
属性修饰。
此外,您可以在运行时使用 Update-TypeData
cmdlet 注册新的类型转换原语。
当然疯狂不止于此,还有专门针对 converting/transform command 参数的额外设施,但这超出了这个问题的范围:)
一些示例代码来说明我在说什么。我有一个使用单一方法的实用程序 class。该方法采用一个参数:一个 System.IO.FileInfo 对象。从 PowerShell 方面,我可以传入一个 System.String 对象,一切都“正常工作”。我很好奇作为开发人员今年越来越多地使用 PowerShell 我很好奇:
- PowerShell 的什么功能允许这种情况发生?
- 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 或多或少是一种“真正的 .NET 语言”,但它确实扩展 具有一系列有时与您所了解的行为相冲突的行为的通用类型系统。 NET 的类型系统行为来自 C# 或 VB.NET.
等语言这个附加在 CLR 之上的类型系统层被恰当地命名为 扩展类型系统 (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 旨在为系统管理员和操作员提供帮助 - 他们可能不会过多考虑数据结构设计,但他们主要关注数据的字符串表示(毕竟,这是bash
、cmd
等教他们使用的东西)。
为了在正确的时间从字符串(或任何其他 non-compliant 参数类型)进行有意义的转换,ETS 附带了 a number of facilities and hooks for implicit type conversion,然后运行时会逐一尝试,直到它要么找到有效的转换机制,或失败(此时方法调用也失败):
Built-in 转换器
- 这些优先处理最常见的边缘情况,例如
null
-转换,“truthy/falsy”-to-real-[bool]
转换,标量的字符串化,等等 - 如果没有这些,PowerShell 将是一个交互工作的麻烦。 - 示例:
- 由于 built-in 转换,这些流程控制语句的行为完全符合您的预期:
if($null){ <# unreachable #> }
、do{ Stop-Process chrome }while(Get-Process chome)
- 由于 built-in 转换,这些流程控制语句的行为完全符合您的预期:
- 这些优先处理最常见的边缘情况,例如
自定义类型转换器
- 这些是注册到一个或多个目标类型的具体类型转换器实现 - 这对于修改您带入运行时的复杂类型的绑定行为很有用。
- 示例:
- RSAT ActiveDirectory 模块使用类型适配器在建模特定目录对象的不同数据类型之间进行交换 类 - 允许您将输出从
Get-ADComputer
(非常具体的输出类型)无缝传输到Get-ADObject
(通用输出类型),反之亦然。
- RSAT ActiveDirectory 模块使用类型适配器在建模特定目录对象的不同数据类型之间进行交换 类 - 允许您将输出从
解析转换器
- 如果找不到合适的 built-in 或自定义类型转换器,PowerShell 将尝试在目标类型上使用合适的 return 类型解析
Parse()
方法。 - 示例:
- 转换操作
[timespan]'1.02:15:25'
可以这样成功。
- 转换操作
- 如果找不到合适的 built-in 或自定义类型转换器,PowerShell 将尝试在目标类型上使用合适的 return 类型解析
Constructor-based 次转化
- 如果上述 none 有效,PowerShell 将尝试解析可以使用给定源类型的单个参数调用的构造函数。
- 这就是您的情况 - PowerShell 有效地为您执行
$util.GetFirstLine([System.IO.FileInfo]::new("C:\temp\random-log.txt"))
CTS 转化次数
- 最后,如果 ETS 的所有对话尝试都失败了,它会回退到解决由类型本身定义的隐式(最终是显式)转换。
- 示例:
- 此转换成功,因为
[DateTimeOffset]
类型具有[DateTime]
的隐式转换运算符:(Get-Date) -as [DateTimeOffset]
- 此转换成功,因为
如果上述具体类型转换器已包含在类型数据文件中(参见 about_Types.ps1xml
),或者如果目标类型为 public 并且源定义用 [TypeConverter()]
属性修饰。
此外,您可以在运行时使用 Update-TypeData
cmdlet 注册新的类型转换原语。
当然疯狂不止于此,还有专门针对 converting/transform command 参数的额外设施,但这超出了这个问题的范围:)