Основы Console.Writeline
У меня есть вопрос о следующем коде:
class CurrentDate
{
static void Main()
{
Console.WriteLine(DateTime.Now);
}
}
Документация гласит:
Записывает текстовое представление указанного массива объектов, за которым следует текущий ограничитель строки, до стандартного потока вывода используя указанную информацию о формате.
Итак, мой вопрос: почему WriteLine
знает текстовое представление объекта DateTime
? Я имею в виду, если я создаю свой собственный объект из своего собственного класса, как он узнает, как преобразовать значение в текст? И даже больше, как он знает, что такое ценность? Как вы можете определить "значение" объекта?
Ответы
Ответ 1
Почему WriteLine знает текстовое представление объекта DateTime? Я имею в виду, если я создаю свой собственный объект из своего собственного класса, как он узнает, как преобразовать значение в текст?
Console.WriteLine
имеет набор перегрузок, соответствующий определенным типам (в основном примитивам). Если компилятор не соответствует перегрузке с предоставленным типом, он совпадает с перегрузкой System.Object
(при условии, что вы предоставляете один параметр). Если это произойдет, он проверяет, реализует ли тип IFormattable
, если он это делает, он вызывает IFormattable.ToString(null, Formatter)
. Если это не так, оно вызывает ToString
на вашем объекте. ToString
определяется в System.Object
, от которого наследуются все объекты. Каждый объект, который хочет создать настраиваемое представление, переопределяет поведение по умолчанию, например DateTime
.
Например, скажем, у вас есть класс Foo
со значением строки Bar
, и вы хотите, чтобы Console.WriteLine
печатал что-то значимое, передавая ему Foo
:
public class Foo
{
public string Bar { get; set; }
public override string ToString()
{
return Bar;
}
}
И теперь мы хотим передать его Console.WriteLine
:
public static void Main(string[] args)
{
var foo = new Foo { Bar = "bar" };
Console.WriteLine(foo);
}
Уступит "бар".
Ответ 2
Так как нет перегрузки для Console.WriteLine(DateTime)
, как в вашем случае, перегрузка Console.WriteLine(Object)
вызывается и эта перегрузка вызывает TextWriter.WriteLine(object)
перегрузка, которая реализована как:
IFormattable f = value as IFormattable;
if (f != null)
WriteLine(f.ToString(null, FormatProvider));
else
WriteLine(value.ToString());
Как вы можете видеть, этот метод проверяет, реализует ли этот тип объекта IFormattable
interface или нет. Поскольку Datetime
реализует этот интерфейс, вызывается ваш f.ToString(null, FormatProvider)
. Из этого метода documentation первый параметр:
Нулевая ссылка (Nothing в Visual Basic) для использования формата по умолчанию определенный для типа реализации IFormattable.
И из документа DateTime.ToString(String, IFormatProvider)
:
Если формат имеет значение null или пустую строку ( "), стандартный формат спецификатор "G"
.
Это означает, что представление будет комбинацией ShortDatePattern
и LongTimePattern
, принадлежащих вашему CurrentCulture
Если вам нужен специальный формат для вашего пользовательского класса, вы можете переопределить метод .ToString()
вашего типа, чтобы изменить его поведение.
Ответ 3
Вопреки мнению некоторых людей, DateTime.ToString()
не будет. В .NET объект может иметь два способа "подстроить" себя: переопределить метод string Object.ToString()
и реализовать интерфейс IFormattable
. DateTime
делает оба.
Теперь... Когда вы пытаетесь сделать
Console.WriteLine(DateTime.Now);
выбран void public static void WriteLine(Object value)
(вы можете увидеть его, если вы нажмете Ctrl + Click на WriteLine
в Visual Studio). Этот метод просто вызывает метод TextWriter.WriteLine(value)
, который выполняет следующие действия:
IFormattable f = value as IFormattable;
if (f != null)
WriteLine(f.ToString(null, FormatProvider));
else
WriteLine(value.ToString());
Все это можно легко увидеть с помощью ILSpy и поиска Console.WriteLine
.