Pull to refresh
4
0

Пользователь

Send message
Именно для этого и используется DateTimeOffset. Вы предлагаете сделать соглашение, что всё время — в utc+0 и это будет работать, но ничто никому не помешает нарушить это соглашение и «по старинке» использовать просто Now, и тогда начнётся погружение в удивительный мир отладки распределённых приложений, особенно интересно выглядящий с точки зрения времени/часовых поясов.
То, что для хранения в БД нужно использовать DateTime.UtcNow вместо DateTime.Now — это как минимум сильно спорно. Микрооптимизации не должны влиять на логику работы, ведь это буквально разное время (на какой-то момент показалось, что автор оригинала живёт в часовом поясе UTC+0 и ему всё равно). Да и является ли это вообще оптимизацией, если потом для отображения надо будет каждый раз время переводить в локальное?

И уж как минимум в данном контексте стоило упомянуть о DateTimeOffset.
А узнать точно так же, как «какие эстешн методы объявлены для типа» — вот ровно 1 в 1.
То есть, никак. Экстеншн методы — это всё же дополнительные упрощалки жизни, а не собственное поведение. И да, если нет дополнительной документации или знания проекта, об эстеншенах мы ничего не узнаем, а если брать такое поведение как у вас, мы будем удивлены поведением, которое описано где-то там.

В целом, подход как у вас имеет право на жизнь. Мне он не нравится — он сильно усложняет понимание того, как работают те или иные вещи, но своё применение он может найти. Также есть вопросы к эффективной технической реализации такой штуки.
В мире, где интерфейсы перечислены через запятую, тоже можно, просто делаем лист-наследник

class MyList<T> { ... }

class MyComparableList<T> : MyList<T>, IEquitable<MyList<T>> 
  where T : IEquitable<T> { ... }

А как в вашем «мире без списка интерфейсов» я узнаю, поддерживает ли тот или иной класс сейчас то или иное поведение?
а он падает, потому что не умеет этот тип десериализовывать и конвертер для него не задан
Ну так тип же ничего не должен уметь. Он просто представляет данные, а «умеет» — сам механизм. И в итоге, всё работает без конвертеров. И рекурсивно в том числе.
Ну как же, вот в одном месте все собрано, без реализаций! На практике же как вы наверное знаете — не особо удобно.
Хэдер файлы — это совсем неудобно. Но они тут не причём, все интерфейсы в C# собираются вместе с реализацией. А то, что вы показываете по ссылке — нечитаемо, и больше похоже как раз на хэдер файлы.
Нет, можно просто написать foo as MyDesiredInterface и посмотреть, работает или нет.
Оно всегда работает. Просто результат работы разный.
Вопрос в open-closed принципе.
Ну так сначала вопрос был в копипасте же. В остальном спорить не буду — такой подход может быть, хоть он, мне кажется, противоречит концепции C# и может быть сложен в понимании и реализации. Например, что, если мне не нужны все стандартные типы? Сидеть и писать реализации для всяких там short и ushort, когда мне они вообще не нужны — это прямая противоположность изначальной идее, когда мы хотим уменьшить объём кода. Тут уж лучше написать 16 перегрузок, честное слово. (точнее, их будет меньше, т.к. в конечном коде нужно ограниченное количество типов)
Да возьмите тот же LINQ: у вас 20 перегрузок метода Sum. А должнен быть ровно один генерик метод который требует только SGroup.
Нельзя не согласится. Но вот только чем это отличается от условного INumber? Зачем отдельный механизм? Как не добавили сразу интерфейс — так не добавили бы шейп, и наоборот.
Не хватает понимания абстракции всей системы. Что есть «пользователь», в частности. Но, за редким исключением, здесь следует выбрать абстрактный класс.

Например. У меня может быть класс Contact — скажем, некий человек-контактное лицо компании. У него тоже есть имя и фамилия. По всем признакам, он удовлетворяет IUser, и я могу без труда использовать его. На следующем шаге я понимаю, что у пользователя (IUser) вообще-то должен быть логин и пароль, секретное слово, признак заблокированности, всё в таком духе. Я добавляю их в IUser, и теперь мне придётся как-то думать о том, как мне жить с тем, что у меня контактное лицо, которое не является пользователем системы, теперь должно иметь пароль.

Вообще же, тут не нужен ни абстрактный класс, ни интерфейс. Это просто тип с данными. А то, что пользователь — Azure можно выразить чезер свойство UserType.
Ответ на этот вопрос я привёл отдельной строкой. Ничем, кроме слов, увы, я помочь не могу. Также, как и дать понимание того, что такое абстракции и зачем они нужны.
И чтобы мне не надо было писать DeserializeIntFromConsole/DeserializeDoubleFromConsole…
По сути, вам это будет всё равно нужно писать, просто это будут не «врапперы», а «расширения». Но окей, тут я понял что вы имеете в виду.
Вам не приходило в голову, что ошибки Json.Net по тому что «Не могу десериализовать» могли бы быть ошибками компиляции? В языках, где интерфейсы не прибиты гвоздями к типам, их реализующих, так и есть.
Мне не приходило в голову то, что ошибки времени исполнения могут быть ошибками времени компиляции, да) Ну то есть, вот у вас есть приходящая строка. Как вы хотите гарантировать, что она будет в корректном формате в момент компиляции?
Про прибитые интерфейсы в C# вы что-то странное пишете. Видимо, имелось в виду, что нельзя для чужих типов определять свои интерфейсы. Ну так наследуемся и определяем.
Зачем мне нужно, чтобы я мог соединить 2 библиотеки вместе и не писать клей в своем приложении, а чтобы оно просто заработало? Действительно, зачем.
Давайте так: либо конкретно, либо вовсе не отвечать. Демагогию разруливать нет никакого желания. Соединяйте библиотеки, на здоровье.
Документация, IDE.
То есть, вместо простого списка — целая документация? Спасибо, я как-нибудь по старинке.
Нам обычно не нужно знать все контракты, которые реализует тип (впрочем, это обычно написано в модуле, где он объявлен). Нам важнее, реализует ли он контракт Х или нет.
Разумеется, и это мы можем обнаружить, глядя на список всех контрактов) Если контракты большие — можно разбить файл на несколько, на каждый оставив один контракт.
А я вот не хочу 20 перегрузок, я хочу чтобы из коробки работало со всеми типами.
Это как? Волшебным образом среда сама поймёт, как каждый из типов должен работать?
Вышло бы что-то вроде:
И ничего качественно не изменилось. Тот же набор методов, та же копипаста.
Тут мы не объявляем 10 типов просто чтобы выразить простую идею
Я вам вынужден сказать, что изначально не надо было объявлять 10 типов. Все методы можно уместить в одном. Более того, изначально вы хотели уйти от копипасты, но теперь у вас не 10 методов, а 20.
Хотя щас подумалось, что в конкретном случае можно было бы обойтись просто пачкой методов расширений. Но в общем случае это задачу не решает
Угу, и тоже в одном типе. Собственно, мне и интересно, какая задача должна решаться и каким образом. Но, видимо, пока не узнаю.
Навскидку
— Это неэффективно с точки зрения компиляции.
— Это неочевидно для читающего — часть методов будут неизвестно зачем. Видимо, это придётся документировать.
— Это усложнит поиск мёртвого кода.
— Это усложнит отладку — я удалил метод расширения (!), но у меня повалилиась компиляция (!) из-за того, что где-то теперь не удовлетворяется контракт, о котором я вообще ничего не знал.
— Операторы is и as, если вообще как-то с ними заработает, будут медленными. Вместо простой проверки типа, нужно перебирать все методы.

И всё это — ради чего? Ради того, чтобы гордо не написать, что я реализую контракт в то время, когда это буквально то, что я делаю?
Чтобы на этапе компиляции понимать, подходит ли данный объект под требования контракта.
Я один привел.
Т.е. вы писали свой сериализатор, работающий только с модифицированной версией int? И в каждом проекте, который этот сериализатор использует, нужно снова и снова его реализовывать?
Библиотеке А(ну например, монгодрайверу) понадобилось расширить инт и они сделали свой враппер. Другой библиотеке (скажем, серилогу) понадобилось расширить инт и они сделали свой инт. Теперь когда мне нужно получить оба расширения, чтобы обе библиотеки работали — что мне делать?
Так зачем вам эти оба расширения? Каждый раз я не могу понять, какой смысл во всём этом «мне нужно». Библиотеки работают и так, они же реализовали всё уже.
То что тип должен перечислять все вещи которые «реализует» — это порочная практика старых языков.
Читающий, догадайся сам, зачем тут это? Или как вообще должна работать схема «контракт-реализация»?
Сценариев использования всегда больше, чем можно придумать, проектируя тип.
Поэтому есть расширения.
Особенно интересно будет, если интерфейс из популярной библиотеки типа Json.Net — добавят?
Нет, конечно. Потому что это не нужно. Нет необходимости пихать всё в базовые типы. Я думал, имеются в виду адекватные интерфейсы, а не всё подряд.
а про практическую сторону «как мне писать код без копипасты». Получается, что без написания врапперов на каждый чих — никак.
Так чем «врапперы» не угодили-то? И тот же самый «чих» будет с расширениями типов.
Буквально на прошлой неделе в статье где я писал мне пригодилось бы расширить дейттайм методами GextNextYear/GetNextMonth, но мне пришлось писать портянку из имплиситов.
Речь, я так понимаю, про это? Как бы вы написали это с шейпами и без портянки?
Я, конечно, всё-таки о реальном кейсе спрашивал, а не о том, что делать, если вы решите изобрести своё сложение) Я про те самые 90% кейсов.
а что делать с типами, к которым майкрософт забыл приделать все возможные интерфейсы?
Если прям забыл — заводить issue, очевидно.
Что делать если мне нужен мой ICustomNumber и чтобы System.Int32 его реализовывал?
Так зачем это вообще может понадобится? Я всё ещё не понимаю. Создайте свой тип просто.
Как мне теперь добиться чтобы инт его реализовывал?
Никак. Потому что это не задача инта — знать как самого себя десериализовывать. Его задача — ну, быть числом. Для десериализации инта из строки нужен отдельный тип, который будет знать как это делать, и это будет его задачей. Там же будут решаться вопросы о том, что делать, например, если string == null, либо совсем не число и т.п.
Так в чём пробема-то? Расширять чужие типы можно при помощи наследования или методов раширения. Подскажите какой-нибудь кейс, где шейпы прям необходимы?
Вы пропускаете основное — концептуальное отличие между ними. Интерфейс — это контракт, то есть что может делать объект. Абстрактный класс — это то, чем является объект. Всё остальное — просто следствия из этого. И множественное наследование, в частности. Объект может одновременно уметь ходить и летать, но не может быть одновременно животным и машиной.

Это всё в итоге даёт ответ, где то или иное изменение должно происходить.
Так мы его не используем. А abstract мы можем использовать для того, чтобы указать, что статический член — это часть контракта, т.е. реализация его ложится на реализующий тип.
Помимо того, что это технически как минимум труднореализуемо, какой в этом вообще смысл? Статические члены вызываются явным указанием типа.
Да, этого бы хватило, если бы парой лет лет раньше не выкатили default interface implementation и разрешили использовать static-методы внутри интерфейсов как вспомогательные.
Только default interface implementation тут не причём, а static методы можно использовать и не внутри интерфейсов.
А так логика действительно хромает: инстанс-методы объявляются без всяких модификаторов, а для статических, будь добр, добавь слово abstract.
Это же разные вещи. Инстанс методы все по умолчанию переопредеяемы. Static методы всегда не-переопределяемы (на данный момент).

Данное предложение — о том, чтобы ввести специальный отдельный тип static методов. Если не вводить отдельное ключевое слово, то как наследуемые типы поймут, что это нужно реализовать? Сделать всё обязательным для переопределения? Это будет идти в разрез и с тем, как статические члены работают в языке вообще, и как они работают сейчас в интерфейсах в частности.
Через какое-то время сделают abstract опциональным для инстанс-членов.
Я тут открою секрет — так уже и есть. Можете проверить.
А потом и обязательным.
В этом нет необходимости.
И мы окончательно провалим вопрос на собеседовании: «чем абстрактный класс отличается от интерфейса»? ;)
А использование ключевых слов никак не влияет на ответ на этот вопрос
Так там была «максимально точная/корректная/близкая к оптимальности регулярка»? Можно её посмотреть? А на код-замену? И на бенчмарк, показывающий ускорение в 10+ раз?
Разумеется, регулярки нельзя пихать куда попало. Тут можно привести куда более простой пример: является ли строка равной другой строке. Конечно, простое сравнение будет куда эффективнее по множеству параметров.

Но тут речь всё же шла об «идеально подобранной регулярке», что всё же исключает очевидные места, где она попросту не нужна.

Information

Rating
Does not participate
Registered
Activity