2015-05-29 5 views
9

Мое понимание синтаксиса синтаксиса строки PowerShell "$($object)" всегда заключалось в том, что $object отличен до [System.String], который вызывает $object.ToString(). Тем не менее, я заметил это любопытное поведение с классом [DateTime] с использованием PowerShell 4.0 в Windows 8.1.Get-Date cast to string vs ToString()

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, по-видимому, использует стандартное значение по умолчанию.

Возможно, это просто ошибка в классе DateTime, но я больше удивляюсь, что различные методы преобразования объекта в строку производят разные результаты. Каковы правила для литья объекта в строку, если не вызывать ToString()? Является ли класс DateTime просто особым случаем из-за его перегрузки ToString(String)?

+1

У меня нет достаточно для ответа прямо сейчас, но я определенно заметил, что '.ToString()' не всегда дает тот же результат, как и встраивание объекта в строке (я не проверял если вложение всегда такое же, как приведение к '[String]'). Поэтому все, что я могу сказать, это не только '[DateTime]'. – briantist

+1

'.Tostring()' принимает аргумент формата, поэтому, если вам требуется согласованность, всегда '.ToString() 'и предоставить желаемый формат. Между тем, согласился, что 'cast' должен просто называть' .ToString() 'и принимать все, что имеет формат по умолчанию. Не знаю, почему они будут реализовывать его любым другим способом. – johnjps111

+2

[Вероятно, связанный] (http://stackoverflow.com/q/14359053/1630171). Я подозреваю, что кастинг использует en-US как культуру по умолчанию в любом случае. 'ToString()' OTOH использует текущую культуру, как [документально] (https://msdn.microsoft.com/en-us/library/k494fzbf%28v=vs.110%29.aspx). –

ответ

8

Если объект реализует интерфейс 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, либо в версиях PowerShell, отличных от ISE.

+0

В этом контексте я использовал «Перегрузка», чтобы отличить DateTime от 'ToString()' от 'ToString (formatString)'. Я не совсем понимаю разницу между структурой и классом в .NET. Здесь уместно? –

+0

Да, это сбивает с толку. 'ToString()' метод - это метод 'Object.ToString()', который является переопределяемым. Но DateTime ** также определяет ** перегрузки для этого метода, но метод ToString() 'является переопределяющим, а метод ToString (что-то) является перегрузкой **. На самом деле классы и структуры совершенно разные. –