При каких обстоятельствах `object.toString()’ должен возвращать значение null.toString()` возвращает значение null?

#c# #nullable-reference-types

#c# #nullable-ссылочные типы

Вопрос:

.Net Core определяет object.ToString() как public virtual string? ToString();

Это означает, что код, подобный следующему, вызывает CS8602 предупреждение «Разыменование ссылки, возможно, нулевой»:

 object t = "test";
var s = t.ToString();
Console.WriteLine(s.Length); // Warning CS8602
 

Это легко исправить, написав s!.Length или t.ToString()!; , но мой вопрос:

При каких обстоятельствах было бы правильным возвращаться null из реализации object.ToString() ?


Ответ, по-видимому, таков: «Вы никогда не должны возвращать значение null из object.toString ()», что достаточно справедливо, но это вызывает другой вопрос: «в таком случае, почему Microsoft объявила это как public virtual string? ToString(); «?


В сторону:

Некоторые комментарии ниже предполагают, что, поскольку реализация может неправильно возвращать значение null, возвращаемое значение должно быть объявлено как string? .

Если это было правдой, то почему та же логика не применяется к ICloneable.Clone() , который не объявлен как возвращаемый object? ?

И, конечно же, эта логика применима к каждому отдельному интерфейсному методу, который возвращает ссылочный тип? Любая реализация такого метода (например, ICustomFormatter.Format() ) теоретически может возвращать null — и, следовательно, возвращаемое значение должно быть обнуляемым. Но это не так.


Прочитав ссылку, предоставленную DavidG, я полагаю, что обсуждение в этой теме отвечает на вопрос к моему удовлетворению:

https://github.com/dotnet/coreclr/pull/23466

Комментарии:

1. «При каких обстоятельствах было бы правильным возвращать значение null из реализации object. toString()?» Никогда, но возможно, что вы это сделаете.

2. Я не думаю, что для этого есть приемлемые обстоятельства, но поскольку «string» имеет значение null, и любой может написать свою собственную реализацию toString для своего объекта… Это все еще возможно…

3. Раздел примечаний буквально говорит вам не возвращать значение null. Я понятия не имею, почему это объявлено string? .

4. Здесь есть некоторые обсуждения по этому поводу

5. Интересная цитата из Стивена Тоуба: «К вашему сведению, у нас есть API в .NET Core, которые возвращают null из toString»

Ответ №1:

Я не вижу причины, по которой кто-то хотел anything.ToString() бы когда-либо вернуться null , но кто знает?

Тогда, я думаю object.ToString() , определяется как возвращающий a string? по соображениям совместимости: это существует с версии v1 .NET и всегда определялось как возвращающий a string , а a string , являющийся ссылочным типом, может быть null. В «современном» объявлении просто указано, что: существует вероятность, что возвращаемая строка может быть нулевой.

И помните, что эта string? вещь является очень недавней и просто метаданными: даже если она была введена string (как это было в более старых версиях), реализации все равно могут возвращать значение null.

Другими словами, совершенно новые API могут (и должны) использовать аннотации с нулевым значением, как они хотят, но повторный ввод существующих библиотек должен учитывать то, что сделали библиотеки.

Я говорю повторный ввод, потому что на самом деле это не ввод текста, а просто аннотации, указывающие ожидаемое поведение. Это похоже на то, как Typescript «аннотирует» код Javascript: базовая система типов все еще старая.

Комментарии:

1. Итак, по этой логике, почему это не ICloneable.Clone() определяется как public object? Clone(); ? Это не так — это определяется как public object Clone();

2. @MatthewWatson: Я не сотрудник Microsoft, поэтому я могу только догадываться, но мое предположение заключается в следующем: контракт для IClonable.Clone() всегда был «Создает новый объект, который является копией текущего экземпляра.» , что подразумевает, что возвращаемое значение не равно нулю, потому что новый объект никогда null (даже если не было поддержки компилятора для обеспечения этого во времена C # 1.0). Контракт для object.ToString() just гласит: «Возвращает строку, представляющую текущий объект.» , что является менее строгим.

3. @Heinzi Ну, null не является строкой (null не имеет никакого типа), поэтому я думаю, что «возвращает строку» означает, что она не должна возвращать значение null. И если вы посмотрите документацию для object.ToString() для ВСЕХ версий .Net, в ней говорится Your ToString() override should not return Empty or a null string.

4. @MatthewWatson: Я понимаю вашу точку зрения. Теперь я мог бы утверждать, что «должен» — это всего лишь рекомендация (и что фраза «нулевая строка» подразумевает, что null действительно считается допустимым значением для строки), но я предполагаю, что это было бы непросто, когда реальной причиной, вероятно, является прагматизм: есть много известных случаев, когда object.ToString() возвращаетnull, тогда как известно мало (если вообще есть) случаев, когда ICloneable.Clone() возвращает null.

5. Поскольку обсуждение в github.com/dotnet/coreclr/pull/23466 демонстрирует, что эти вопросы также обсуждались внутри Microsoft, пункт, который было рекомендовано реализовать ToString как возвращающий ненулевую строку, действительно был вызван pro- string? . Я полагаю, что эти случаи обсуждались в каждом конкретном случае, чтобы сбалансировать совместимость с наилучшей практикой… Также обратите внимание, что object.ToString() возврат string? не препятствует возврату переопределений string (об этом я узнал в том же выпуске GitHub).