Здравствуйте, Qbit86, Вы писали:
Q>Даже уже в детском саду рассказывают, что исключения не надо использовать для normal control flow.
Исключения не являются частью нормального control flow.
Но вот что конкретно означает этот Try в TryFormat? Доки — нет. Если только из-за размера буфера, то это странно. Или намек на то, что недостаточный буфер — это нормальный control flow. Х.з. но с первого взгляда выглядит по-делитански.
Вот подобные вещи и ставят под угрозу всю концепцию .NET 5. На словах — красиво. Но если под копотом будет подобные болезни — не взлетит.
Здравствуйте, Qbit86, Вы писали:
Q>То же, что и во всех остальных API, доступных сто лет: TryGetValue, TryParse, TryGetBuffer, etc.
Обратите внимание, что все перечисленные API возвращают результат в зависимости от входных данных или текущего состояния обьекта. Что логично и очень понятно.
А в случае TryFormat подразумевается некоторое действие которое может быть выполнено или не выполнено в зависимости от? Предпологаемых выходных данных? Входных данных? Фазы луны? Текущего календаря? Времени жизни нынешнего императора Японии? Здесь нет однозначной интерпретации. И это вызывает когнитивный дисонанс, особенно тот факт что этими неуклюжими TryFormat предлагают облепить почти все базовые типы.
Здравствуйте, Qbit86, Вы писали:
Q>То же, что и во всех остальных API, доступных сто лет: TryGetValue, TryParse, TryGetBuffer, etc.
TryParse — это понятно, там неизвестный вход и потенциальная ошибка преобразования, но это может быть нормальным потоком исполнения. А TryFormat — это тупость. Если Format кидает исключение, то это ошибка в программе.
Здравствуйте, AlexRK, Вы писали:
ARK>TryParse — это понятно, там неизвестный вход и потенциальная ошибка преобразования, но это может быть нормальным потоком исполнения.
В TryFormat ровно то же самое: неизвестный вход, который может как поместиться в буфер, так и не поместиться. Оба случая — нормальный поток выполнения.
Span<char> buffer = stackalloc char[8];
if (number.TryFormat(buffer, out int charsWritten))
textWriter.WriteLine(buffer.Slice(0, charsWritten));
else
textWriter.WriteLine("Failure");
Здравствуйте, Aquilaware, Вы писали:
A>Обратите внимание, что все перечисленные API возвращают результат в зависимости от входных данных или текущего состояния обьекта. Что логично и очень понятно.
В случае TryFormat ровно то же самое, в зависимости от входных данных.
A>А в случае TryFormat подразумевается некоторое действие которое может быть выполнено или не выполнено в зависимости от? Предпологаемых выходных данных? Входных данных? Фазы луны? Текущего календаря? Времени жизни нынешнего императора Японии? Здесь нет однозначной интерпретации.
Однозначная интерпретация есть — в зависимости от входных данных. Шутка про императора Японии вообще не к месту.
Здравствуйте, Qbit86, Вы писали:
ARK>>TryParse — это понятно, там неизвестный вход и потенциальная ошибка преобразования, но это может быть нормальным потоком исполнения.
Q>В TryFormat ровно то же самое: неизвестный вход, который может как поместиться в буфер, так и не поместиться. Оба случая — нормальный поток выполнения.
Хорошо, пусть это будет нормальный поток исполнения (правда, где в таком случае String.TryFormat, Console.TryReadLine, File.TryReadContent и т.д. и т.п.). Но убожества API это не отменяет — ничто не мешает просто заигнорить результат. В данном случае соображения корректности принесены в жертву "эффективности" (для которой дотнет все равно не предназначен).
Здравствуйте, AlexRK, Вы писали:
ARK>правда, где в таком случае String.TryFormat, Console.TryReadLine, File.TryReadContent и т.д. и т.п.
Это нужно в каждом отдельном случае рассматривать, насколько это уместно и/или востребовано.
ARK>Но убожества API это не отменяет — ничто не мешает просто заигнорить результат.
У любой функции можно заигнорить результат :xz:
ARK>В данном случае соображения корректности принесены в жертву "эффективности"
Обошлось без жертв — аллоцирующие методы остались на месте.
ARK>для которой дотнет все равно не предназначен
Здравствуйте, Qbit86, Вы писали:
ARK>>Но убожества API это не отменяет — ничто не мешает просто заигнорить результат. Q>У любой функции можно заигнорить результат
Исключение заигнорить нельзя (неявно или случайно).
ARK>>В данном случае соображения корректности принесены в жертву "эффективности" Q>Обошлось без жертв — аллоцирующие методы остались на месте.
Но появилась возможность некорректно применить неаллоцирующий метод.
Span<char> buffer = stackalloc char[1];
number.TryFormat(buffer, out int charsWritten); // oops
textWriter.WriteLine(buffer.Slice(0, charsWritten));
Здравствуйте, AlexRK, Вы писали:
ARK>Исключение заигнорить нельзя (неявно или случайно).
Исключения нежелательно использовать для normal control flow. Если пофиг на аллокации или исключения (и пофиг на пользователей твоей библиотеки), то используй ToString().
ARK>Но появилась возможность некорректно применить неаллоцирующий метод. ARK>
ARK>Span<char> buffer = stackalloc char[1];
ARK>number.TryFormat(buffer, out int charsWritten); // oops
ARK>textWriter.WriteLine(buffer.Slice(0, charsWritten));
ARK>
Так а что тут некорректного? charsWritten равен 0, buffer.Slice(0, charsWritten) будет пустым, стандартные реализации TextWriter'а корректно напечатают пустую строку.
Те же рассуждения применимы и к TryGetValue в словаре. Такой код в общем случае может быть умышленным, и будет вести себя вполне ожидаемым способом.
Здравствуйте, Qbit86, Вы писали:
ARK>>Исключение заигнорить нельзя (неявно или случайно). Q>Исключения нежелательно использовать для normal control flow.
Возможность случайного игнора ошибки тоже нежелательно использовать для normal control flow.
Q>Так а что тут некорректного? charsWritten равен 0, buffer.Slice(0, charsWritten) будет пустым, стандартные реализации TextWriter'а корректно напечатают пустую строку.
Не факт, что программист ожидал именно этого. И компилятор ему не помог.
Q>Те же рассуждения применимы и к TryGetValue в словаре.
Да, верно. Тоже, я считаю, не очень хорошее API. Это бич дотнета, не имеющего алгебраических типов.
Q>Такой код в общем случае может быть умышленным, и будет вести себя вполне ожидаемым способом.
Здравствуйте, Qbit86, Вы писали:
Q>Выглядит как всегда, вообще-то. Стандартный паттерн, какое ещё «делитански».
А можно показать пример корректного использования этого нового API?
А то я как-то не могу пока вьехать, как его применять.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, AlexRK, Вы писали:
Q>>Те же рассуждения применимы и к TryGetValue в словаре.
ARK>Да, верно. Тоже, я считаю, не очень хорошее API. Это бич дотнета, не имеющего алгебраических типов.
Точнее конкретных языков.
В F#, Nemerle, Scala (которая была ещё для .NET) проблем ввести АТД не было.
Здравствуйте, _NN_, Вы писали:
ARK>>Да, верно. Тоже, я считаю, не очень хорошее API. Это бич дотнета, не имеющего алгебраических типов. _NN>Точнее конкретных языков. _NN>В F#, Nemerle, Scala (которая была ещё для .NET) проблем ввести АТД не было.
Здравствуйте, Aquilaware, Вы писали:
A>Но вот что конкретно означает этот Try в TryFormat? Доки — нет. Если только из-за размера буфера, то это странно. Или намек на то, что недостаточный буфер — это нормальный control flow. Х.з. но с первого взгляда выглядит по-делитански.
Да, похоже что только из за размера буфера.
Потому что в случае других ошибок TryFormat выбрасывает исключения: Decimal.TryFormat
System.FormatException if the format is not valid for this data type.
Хотя в том же System.Text.Decoder пара Convert()/GetCharCount() позволяла реализовать какие угодно сценарии.
Convert просто декодит до тех пор пока, есть место в буфере. При следующем вызове продолжает декодить в новый буфер с точки останова.
А если хочется декодить за один раз, то можно заранее вычислить размер буфера вызовом GetCharCount().
Вариант с TryFormat() получился не самым оптимальным по производительности — метод внутри себя сначала вычисляет необходимый размер буфера — TryFormatDecimalF
И в случае недостаточного размера вынуждает делать повторный вызов и повторно выполнять все вычисления и проверки.
Здравствуйте, AlexRK, Вы писали:
Q>>Те же рассуждения применимы и к TryGetValue в словаре.
ARK>Да, верно. Тоже, я считаю, не очень хорошее API.
Это идиоматичный API. Хорошо известный разработчикам с опытом на конкртеной платформе. Тут выше писали, что к нему ещё нет документации — я этого и не знал до текущего треда. Потому что не возникало необходимости заглядывать в справку — и так всё знакомо, понятно и не вызывает удивления.
ARK>Это бич дотнета, не имеющего алгебраических типов.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, Sinclair, Вы писали:
S>>А можно показать пример корректного использования этого нового API?
Q>https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Text/StringBuilder.cs
Спасибо, я как-то так и предполагал, но хотел убедиться. То есть предполагается ровно один паттерн: пробуем втиснуться в буфер, какой бы он ни был; если не сработало — фоллбэкаемся на обычный метод с выделением.
Варианта "скорректировать размер буфера" у нас нету, т.к. TryFormat не даёт нам информации о том, сколько места надо.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, sergeya, Вы писали:
S>Да, похоже что только из за размера буфера. S>Потому что в случае других ошибок TryFormat выбрасывает исключения: S>Decimal.TryFormat
S>Вариант с TryFormat() получился не самым оптимальным по производительности — метод внутри себя сначала вычисляет необходимый размер буфера — TryFormatDecimalF S>И в случае недостаточного размера вынуждает делать повторный вызов и повторно выполнять все вычисления и проверки.
Да уж. Я ж говорил — дилетантсво. Им бы это нутро пока лучше спрятать в internal. А то получается смесь коровы и собаки, и еще так гордо покакано во все базовые типы.
Здравствуйте, Sinclair, Вы писали:
S>Спасибо, я как-то так и предполагал, но хотел убедиться. То есть предполагается ровно один паттерн: пробуем втиснуться в буфер, какой бы он ни был; если не сработало — фоллбэкаемся на обычный метод с выделением.
Отчего ж один, можно еще в бесконечном цикле долбиться, увеличивая на единицу размер буфера, пока не влезет. Производительность!