将 Get-Date 强制转换为字符串与 ToString() 的区别

11

我对PowerShell的字符串嵌入语法"$($object)"的理解一直是,$object被强制转换为[System.String],从而调用$object.ToString()。然而,我在Windows 8.1上使用PowerShell 4.0时注意到了[DateTime]类的奇怪行为。

PS> $x = Get-Date

PS> $x.GetType() | select -ExpandProperty Name
DateTime

PS> $x.ToString()
2015-05-29 13:36:06

PS> [String]$x
05/29/2015 13:36:06

PS> "$($x)"
05/29/2015 13:36:06

似乎"$($object)"的行为与强制转换为字符串相同,但显然与$object.ToString()产生不同的结果。$x.ToString()与intl.cpl中设置的短日期格式一致(yyyy-MM-dd)。[String]$x似乎使用en-US默认格式。

这可能只是DateTime类中的一个bug,但我更惊讶于将对象转换为字符串的不同方法会产生不同的结果。如果不是调用ToString(),则将对象转换为字符串的规则是什么?DateTime类是否只是由于其ToString(String)的重载而是一个特例?


1
我现在没有足够的答案,但是我肯定注意到.ToString()并不总是产生与将对象嵌入字符串相同的结果(我还没有测试嵌入是否始终与转换为[String]相同)。所以我只能说它不仅限于[DateTime] - briantist
1
.ToString() 方法接受格式参数,因此如果您需要一致性,请始终使用 .ToString() 并提供所需的格式。与此同时,同意 cast 应该只调用 .ToString() 并接受其默认格式。不确定为什么他们会以其他方式实现它。 - johnjps111
2
可能相关。我猜测强制转换使用en-US作为默认文化方式。而ToString()则使用当前文化,正如文档所述 - Ansgar Wiechers
1
在你的机器上,Get-Culture 的输出是什么? - Matt Johnson-Pint
1
嗯...我用$x.ToString()得到了mm/dd/yyyy格式。虽然我也得到了12小时制的时间,但是使用[String]$x可以得到24小时制的时间。值得注意的是,从DateTime转换为[String]必须是PowerShell支持的内容,因为它对于C#或其他.NET语言无效。 - Matt Johnson-Pint
文化是1033 en-US,但我已经使用控制面板更新了日期(yyyy-MM-dd)和时间(HH:mm:ss)格式。 - Ryan Bemrose
2个回答

9
如果一个对象实现了 IFormattable 接口,那么 PowerShell 在进行转换操作时会调用 IFormattable.ToString 而不是 Object.ToString。对于静态的 Parse 方法也同样适用:如果有带有 IFormatProvider 参数的重载方法,则会被调用。
Add-Type -TypeDefinition @'
    using System;
    using System.Globalization;
    public class MyClass:IFormattable {
        public static MyClass Parse(string str) {
            return new MyClass{String=str};
        }
        public static MyClass Parse(string str,IFormatProvider fp) {
            return new MyClass{String=str,FormatProvider=((CultureInfo)fp).DisplayName};
        }
        public string String {get;private set;}
        public string FormatProvider {get;private set;}
        public override string ToString() {
            return "Object.ToString()";
        }
        string IFormattable.ToString(string format,IFormatProvider fp) {
            return string.Format("IFormattable.ToString({0},{1})",format,((CultureInfo)fp).DisplayName);
        }
    }
'@
[String](New-Object MyClass) #Call IFormattable.ToString(null,CultureInfo.InvariantCulture)
[MyClass]'Test'              #Call MyClass.Parse("Test",CultureInfo.InvariantCulture)

4

您的问题不是PowerShell问题,而是.NET问题。PowerShell脚本可以直接使用.NET结构[datetime],即PowerShell不会改变其行为。

如果没有调用ToString(),将对象强制转换为字符串的规则是什么?

强制转换使用与文化无关的定义。ToString()方法可能包含文化相关的实现,因为它是可重写的。

DateTime类是否只是因为其重载ToString(String)而成为特例?

首先,DateTime不是一个类,它是一个结构体。 其次,在这种情况下,不存在ToString()方法的重载;正确的说法是覆盖(它覆盖了Object.ToString()方法)。

为了更好地理解我的意思,请尝试在不同的文化中运行以下有趣的日期和时间打印(复制、粘贴并运行):

function f{
    $x=get-date
    [CultureInfo]$currentCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('en-US')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('ar-IQ')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('de-DE')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('ru-RU')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('fr-FR')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('zh-CN')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('zh-HK')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('zh-TW')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('hu-HU')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('ko-KR')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('ja-JP')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('ka-GE')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=[CultureInfo]::CreateSpecificCulture('pt-BR')
    $x.ToString()
    [System.Threading.Thread]::CurrentThread.CurrentCulture=$currentCulture
}

f

请注意,上述代码在运行时将会产生不同的输出结果,具体取决于是否在ISE版本或非ISE版本的PowerShell中运行。

我在这个上下文中使用了“重载”来区分DateTime的ToString()ToString(formatString)。我并不完全熟悉.NET中结构体和类之间的区别。这在这里是否相关? - Ryan Bemrose
是的,这很困惑。 ToString() 方法是 Object.ToString() 方法,可以进行重写。但是,DateTime 也定义了 这个方法的重载,但是 ToString() 方法是一个重写,而 ToString(something) 方法是一个 重载。类和结构是非常不同的。 - user4018458

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接