Рекомендации по неявному преобразованию .Net

Каковы общие рекомендации относительно того, когда определяемое пользователем неявное преобразование может, должно или не должно определяться?

Я имею в виду такие вещи, как, например, «неявное преобразование никогда не должно терять информацию», «неявное преобразование никогда не должно вызывать исключения» или «неявное преобразование никогда не должно создавать экземпляры новых объектов». Я почти уверен, что первый правильный, третий - нет (или мы могли бы иметь только неявное преобразование в структуры), а о втором я не знаю.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
1 986
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я согласен с первым определенно - со вторым в большинстве случаев («никогда не говори никогда»), но не буду волноваться от третьего; помимо всего прочего, он создает ненужное различие между структурами и классами. В некоторых случаях наличие неявного преобразования может значительно упростить соглашение о вызовах.

Для явных преобразований возможно все.

Однако не так часто нужно писать операторы преобразования. И во многих случаях явные операторы более полезны с учетом того, что они могут нарушиться, если условия не верны (например, с Nullable<T>, если у них нет значения).

Ответ принят как подходящий

Первый не так прост, как можно было ожидать. Вот пример:

using System;

class Test
{
    static void Main()
    {
        long firstLong = long.MaxValue - 2;
        long secondLong = firstLong - 1;

        double firstDouble = firstLong;
        double secondDouble = secondLong;

        // Prints False as expected
        Console.WriteLine(firstLong == secondLong);

        // Prints True!
        Console.WriteLine(firstDouble == secondDouble);        
    }
}

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

Обновлено: просто чтобы немного ответить на вопрос, вероятно, стоит прочитать операторы преобразования - часть рекомендаций по проектированию библиотеки классов Microsoft.

Ой! Что ж, это скорее подсказывает мне, что это неявное преобразование немного не работает.

Alexey Romanov 24.12.2008 00:52

Вы действительно погружаетесь в странности, не так ли ?? Откуда вы взяли это? :)

Andrew Rollings 24.12.2008 00:59

@Andrew: ну, у них обоих 64 бита, и не каждое двойное число является целым числом. Разумеется, будут некоторые целые числа, которые невозможно представить в точности.

Jon Skeet 24.12.2008 01:29

Не считаю удивительным тот факт, что firstDouble==secondDouble. Что я считаю "удивительным", так это то, что firstLong == secondDouble компилируется без диагностики и возвращает истину. Нельзя ожидать, что назначения firstDouble и secondDouble сделают что-либо, кроме как заставить firstDouble содержать лучшие доступные double представления firstLong и secondLong. Для сравнения, поскольку == имеет перегрузку, первый тип оператора которой требует преобразования, а другой - нет, я бы предпочел диагностику компилятора.

supercat 03.09.2013 00:33

@supercat: в обоих случаях преобразование неявное, но может потерять информацию. Я бы хотел, чтобы оба преобразования вызывали предупреждение / ошибку, или ни то, ни другое.

Jon Skeet 03.09.2013 00:35

По идее, нет причин, по которым == должен преобразовывать свои аргументы в один и тот же тип; некоторые перегрузки смешанного типа могут иметь значения, недоступные другими способами [например, (лонг, улонг) и (улонг, лонг)]. Я бы сказал, что, когда тип назначения очевиден, «нечеткое» преобразование без приведения типов яснее, чем преобразование с использованием, поскольку большинство явных приведений типов гарантирует, что в случае успеха исходные значения будут точно представлены в типе назначения. Если сказать, что double X = someLong;, то будет ясно, что X будет значением, подходящим для double, независимо от того, подходит ли someLong.

supercat 03.09.2013 00:45

99% всего, что делают компьютеры, «теряет информацию». Удаление информации который никогда не понадобится - это хорошо. Как вы думаете, g.DrawLine(myPen, (float)myX1, (float)myY1, (float)myX2, (float)myY2) чище, чем g.DrawLine(myPen, myX1, myY1, myX2, myY2)? Дополнительная точность double может иметь отношение к окружающему коду, но не имеет отношения к DrawLine. ИМХО, foo=something; должен компилироваться без диагностики, если это, вероятно, заставит foo содержать лучшее или, по существу, одинаково хорошее представление всего, для чего something имеет лучшее представление.

supercat 03.09.2013 00:52

@supercat: Опять же, я думаю, что мы не собираемся соглашаться, и темы комментариев SO не являются подходящей средой для обсуждения. Если мы когда-нибудь встретимся лично, будет о чем поговорить ...

Jon Skeet 03.09.2013 01:01

Я бы сказал, что для любой пары значений типов T и U, если var myT = (T)someU; return (myT == otherU) не должен возвращать ничего, кроме (someU == otherU). Если он не может этого обещать, он должен генерировать исключение при явном приведении типов или вообще не компилировать. Однако, если T - это float, а U - это double, это не сработает. При преобразовании в float, явном или неявном, говорится: «Удалите младшие биты, поскольку они мне не нужны». Однако использование float в качестве double предполагает, что биты, вероятно, все-таки нужны - их просто не существует (упс).

supercat 03.09.2013 01:07

@supercat: Пожалуйста, посмотрите мой предыдущий комментарий. Я действительно нет собираюсь втянуться в это.

Jon Skeet 03.09.2013 01:08

Справедливо. Я предполагаю, что моя первоначальная точка зрения заключалась в том, что что касается вашего примера [причина для комментария здесь], даже люди, которые понимают, как работает double, и не удивлены firstDouble == secondDouble, могут все еще находить firstDouble == secondLong удивительным (поскольку не очевидно, что он должен быть эквивалентен firstDouble == (double)secondLong, а не (ExtendedDouble)firstDouble == (ExtendedDouble)secondLong).

supercat 03.09.2013 01:12

Пограничный случай, который заставил меня задать этот вопрос, является нарушением второго. А именно, у меня есть класс Lazy <T> (разве не все?), И я начал размышлять, не следует ли мне выполнять неявное преобразование в T. Мой инстинкт - сказать да, и текущая версия делает это, но я не слишком Конечно.

Нет, я действительно не стал бы - поскольку вы потенциально делаете значительную работу, кажется неправильным делать это неявным. Возможно, у вас есть преобразование явный - но если вы ищете, "почему это внезапно выполняет какой-то код?" неявное преобразование слишком легко пропустить.

Jon Skeet 24.12.2008 01:57

Поразмыслив еще немного, я все-таки склонен согласиться.

Alexey Romanov 24.12.2008 02:24

Или даже такой метод, как GetValue ()

Marc Gravell 24.12.2008 13:39

Await () в моем случае, унаследованный от Future <T>

Alexey Romanov 24.12.2008 18:33

Если вы будете поддерживать неявные преобразования между объектами ваших собственных типов, я бы посоветовал вам решить, какие «аксиомы» следует применять к таким преобразованиям (например, может быть полезно решить, что если все a==b, b==c и a==c являются допустимо, и два из них верны, третий также должен быть верным), а затем определите наиболее полезный набор преобразований, которые позволят сохранить эти аксиомы. Я обсуждаю четыре полезные аксиомы в своем блоге на http://supercatnet.blogspot.com/2013/09/axioms-of-implicit-type-conversion.html (к сожалению, .NET Framework включает преобразования, которые нарушают все четыре, но запрещает преобразования, которые не должны были бы).

Важно учитывать, что неявные преобразования неточных типов к в большинстве контекстов представляют меньший риск удивления, чем преобразования неточных типов из, но есть заметное исключение: если в метод или оператор передается что-то более точного типа. который имеет перегрузки, которые принимают как более точный тип, так и менее точный тип, в который он может быть преобразован, некоторый уровень «удивительного» поведения будет почти неизбежен, если не сделать неявное преобразование невозможным [например, поведение, если программист записывает if (someLong == someFloat), может быть удивительным, но не потому, что потеря точности при неявном преобразовании long в float удивительна, а потому, что существует как минимум шесть разных способов сравнить long и float (*) , и любой, означающий, что компилятор может подключиться к прямому сравнению, удивит тех, кто ожидал чего-то другого. Единственное известное мне решение, позволяющее избежать такого удивления, - это предоставить перегрузки для явного охвата всех неоднозначных случаев и пометить их тегом [Obsolete()]. Это может быть несколько неудобно, но даст существенное преимущество, заключающееся в возможности удовлетворить все четыре аксиомы.

(*) Программист, вероятно, намеревается проверить, равняется ли long значению числа с плавающей запятой, округленному до ближайшего, усеченному до нуля или уменьшенному до отрицательной бесконечности; В качестве альтернативы, программист может захотеть проверить, является ли float тем, который представляет long, имеют ли float и long одинаковую номинальную стоимость или оба float и long будут конвертироваться в один и тот же double.

Другие вопросы по теме