Comments 167
Ну вот кто-то счёл, что ваш комент образец трэша и неадеквата и намекнул вам об этом минусом.
И если создатели плывут в сторону от строгой объектно-ориентированности
… которой там никогда не было.
Ну ладно, ок, первые версии еще ничего. Но как только у вас появились inline delegates, точно стало понятно, что это язык со смешанной парадигмой. А LINQ — следующий шаг в этом направлении. И дальше, дальше, дальше.
Ну не нравится вам, ок, не нравится. Вы пишете, что вам не нравится. Кому-то не нравится ваш комментарий, и они выражают свое отношение к вашему комментарию тем способом, который на этом ресурсе принят.
Минусы без аргументации почему — это ничто.
Минус без аргументации обычно означает «я искренне считаю, что вы написали хрень, но диалог с вами мне не настолько интересен, чтобы раскрывать подробно свою мысль». Вполне себе адекватный ответ.
ресурс без треша и неадеквата.
Ну, вы не перегибайте так.
Например, в описанной Вами ситуации многие не считают разумным тратить время и усилия почитать
Те, кто не читает, вообще никакого мнения про ваш текст не имеют, поэтому вполне логично, что ни минусов, ни плюсов они не ставят :) Я имею в виду тех, кто читает. Мне, например, тема С# 9 интересна, поэтому я прочитал и статью, и комментарии под ней. Ваш комментарий мне тоже абсолютно честно показался каким-то нелепым. Минусы я ставить не могу, да и не стал бы даже если бы мог, т.к. мне это просто не важно, но прекрасно понимаю тех, кто ставит.
Минус без аргументации обычно означает «я искренне считаю, что вы написали хрень, но диалог с вами мне не настолько интересен, чтобы раскрывать подробно свою мысль»
критика должна быть конструктивной, иначе какой в ней смысл
Вообще-то, у критики бывают разные задачи, и не все из них требуют конструктива.
А почему вы противопоставляете критику личному суждению? Это не обязательно взаимоисключающие вещи.
А главное, сверху даже критики-то не обещал никто.
Конструктивная критика — «я не согласен с вашим мнением, т.к. вы не учли такие-то и такие-то моменты»
Первое никому ничего не даст, со второго можно чему-то научиться
Конструктивная критика — «я не согласен с вашим мнением, т.к. вы не учли такие-то и такие-то моменты»
Вот только она при этом все еще может остаться личным суждением. Например, потому что эти моменты важны только по личному суждению критикующего.
Первое никому ничего не даст, со второго можно чему-то научиться
А почему вы решили, что стоит задача кого-то чему-то научить?
Вот только она при этом все еще может остаться личным суждением
я не очень понимаю, как
если мы спорим про программирование, и я, например, говорю что у линковского .find сложность О(log(n)) и поэтому на небольших коллекциях им искать — самое оно, как это может быть личным суждением? Оно ошибочное, и доказать это — минута гугления. Мой оппонент будет прав, даже если лично мне он неприятен
А почему вы решили, что стоит задача кого-то чему-то научить?
или научиться
А иначе зачем ввязываться в дискуссии в интернете? Я могу понять в личном общении, там и поорать можно, и в рыло оппоненту просунуть (или получить), жизнь как есть
А в интернете-то…
если мы спорим про программирование, и я, например, говорю что у линковского .find сложность О(log(n)) и поэтому на небольших коллекциях им искать — самое оно, как это может быть личным суждением?
"Самое оно" — это и есть личное суждение.
Оно ошибочное, и доказать это — минута гугления.
Неа, нельзя доказать, что метод x на небольших коллекциях не самое оно. Можно доказать, что у него другая сложность.
А иначе зачем ввязываться в дискуссии в интернете?
Напомню, что началось все с того, что человек поставил минус именно потому, что он не хотел ввязываться в дискуссию.
если мы спорим про программирование, и я, например, говорю что у линковского .find сложность О(log(n)) и поэтому на небольших коллекциях им искать — самое оно, как это может быть личным суждением?
«Самое оно» — это и есть личное суждение.
Его элементарно можно опровергнуть; а чье-то личное мнение попробуйте опровергнуть, ха
Напомню, что началось все с того, что человек поставил минус именно потому, что он не хотел ввязываться в дискуссию.
ну так оно и выглядит как «сам дурак»
Его элементарно можно опровергнуть
Попробуйте.
ну так оно и выглядит как «сам дурак»
Но свою задачу прекрасно выполняет. О чем, собственно, и речь.
Попробуйте.
docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.find?view=net-5.0
Исходное предположение неверно, эрго, последующий вывод — тоже
Но свою задачу прекрасно выполняет
Какую?
Исходное предположение неверно, эрго, последующий вывод — тоже
Nope. "Я считаю", что на небольших коллекциях сложность не имеет значения, поэтому этот метод — самое оно.
Какую?
Продемонстрировать отношение к высказанному в комментарии тезису.
Nope. «Я считаю», что на небольших коллекциях сложность не имеет значения, поэтому этот метод — самое оно.
ну так это совсем другое исходное предположение, никакого отношения к моему не имеет; соответственно, весь наш спор бессмысленнен
Продемонстрировать отношение к высказанному в комментарии тезису.
т.к. нет никакой обратной связи, это интересно только продемонстрировавшему
ну так это совсем другое исходное предположение, никакого отношения к моему не имеет; соответственно, весь наш спор бессмысленнен
Так и ваш исходный пример не имеет никакого отношения к критике, выраженной в виде личного мнения, так что все логично.
т.к. нет никакой обратной связи, это интересно только продемонстрировавшему
Обратная связь как раз есть: рейтинг комментария. И он интересен не только написавшим, но и остальным. Я вот смотрю на рейтинги.
Так и ваш исходный пример не имеет никакого отношения к критике, выраженной в виде личного мнения, так что все логично.
не, критика в виде личного мнения была бы КГ/АМ, а не ссылки на страницы документации
Обратная связь как раз есть: рейтинг комментария.
Он ничего не выражает и не обьясняет
Может, человек просто не с той ноги встал. Может, он просто не в теме (см. эффект Даннинга-Крюгера). Может, он и имел что-то конкретное в виду, но мы этого не узнаем
А мне бы хотелось не полагаться просто на чье-то мнение, а видеть аргументы сторон, чтоб я сам мог принять решение
не, критика в виде личного мнения была бы КГ/АМ, а не ссылки на страницы документации
Вооот. Это типичный пример личного суждения. Вы считаете, что критика в виде личного суждения была бы [...]. Это, заметим, ваша критика чужого поведения. Иными словами, критика, которую вы даете, не соответствует вашим же требованиям к критике, и при этом подтверждает мой тезис.
Повторюсь еще раз: возможна критика, являющаяся личным суждением, но при этом не сводящаяся к КГ/АМ.
Он ничего не выражает и не обьясняет
Отнюдь. Он выражает некую совокупную реакцию читателей на комментарий. А объяснять он ничего и не должен.
А мне бы хотелось не полагаться просто на чье-то мнение, а видеть аргументы сторон, чтоб я сам мог принять решение
Вам бы хотелось — это ваши личные пожелания, не правда ли?
Вы считаете, что критика в виде личного суждения была бы
Кри́тика (от фр. critique из др.-греч. κριτική τέχνη «искусство разбирать, суждение») — анализ, оценка о явлениях какой-либо области человеческой деятельности[1]. Задачами критики являются:
- выявление противоречий;
- выявление ошибок и их разбор;
- разбор (анализ), обсуждение чего-либо с целью дать оценку (например, литературная критика);
- исследование, научная проверка достоверности, подлинности чего-либо (например, критика текста, критика исторических источников);
- оценка;
И мне удивительно, что меня минусуют прям все, кому не лень.
Неуместная метафора — вы преувеличиваете.
Минусы без аргументации почему — это ничто.
А плюсы?
Проектный или командный code style convention, желательно с проверкой лиентервми.
… а когда чего-то из этого нет, вам язык не поможет все равно. Так что не в языке проблема, нет.
Будет изувечена плохим именованием, помесью табов и пробелов и еще миллионом способов испортить хороший код. Будет изувечена лямбда-выражениями, в конце концов.
И каждое сомнительное нововведение будет уводить от строгой дисциплины, как снежный ком.
Не было там никогда "строгой дисциплины", поэтому и уводить неоткуда.
Красота любой игры в строгих правилах: шахматы, футбол.
Я не знаю про "любую игру", но я знаю, что красота искусства в том, чтобы в нужный момент полностью проигнорировать правила.
Но код — он и не игра, и не искусство, он просто средство достижения цели. Слишком строгие правила усложняют достижение цели, а не способствуют ему.
Можете подробнее раскрыть, чем вам не нравятся лямбда-выражения, что вы считаете, что их использование изувечит код?
И у меня есть предчувствие, что эффективность кода при этом снижается, тяжеловесность растёт.
Не знаю как в мире C#, но в мире PHP, например, принято при выходе новых синтаксических и подобных фич сравнивать не только код "было"-"стало", но бенчмарки прогонять.
А в наличии только одного оптимального варианта я очень сомневаюсь. Хотя бы потому что метрики оптимальности/эффективности могут быть разными: тут нам важны циклы процессора, тут потребляемая память, тут отсутствие блокировок, а тут гарантии времени выполнения.
Это не вспоминая о том, что для большинства бизнесов, платящих деньги программистам, одна из самых важных метрик: скорость решения задачи командой разработки, календарные сроки от постановки задачи до выхода в эксплуатацию.
Поэтому, вне зависимости от ОС и процессора можно ±спрогнозировать эффективность кода.
… и выяснить, что прогноз бесконечно далек от реальности, потому что вы не учли работу рантайма.
Другое дело, если Вы пишете конкретный проект под конкретного заказчика.
Ну вот я не пишу конкретный проект под конкретного заказчика. Я даже версию Windows, на которой будет код выполняться, не всегда точно знаю.
В этом случае гораздо проще оценить предполагаемое быстродействие, например.
Оценить его и сейчас можно, в этом отношении ничего не изменилось.
Как это связано с тем, что я сказал?
облегчают читаемость и уменьшают многословность
Программисты 90% времени читают код, лишь 10% пишут, поэтому мне кажется, что зря настолько легко отмахиваетесь от аргумента "основная работа становится проще".
А если ещё дедлайн. Безусловно, в этом случае новые фичи — ценная вещь.
Ровно наоборот. Если у вас дедлайн, хвататься за новый фичи можно только в том случае, если без них никак, во всех остальных случаях надо сидеть на том, что уже известно.
а это именно надстройки над языком
Да нет, это сам язык и есть. Как вы отличаете "язык" от "надстроек"?
Конечно, сорри, но мы как собаки Павлова…
Нет, мы не как собаки Павлова.
Есть классика, базовые конструкции языка.
Что такое "базовые конструкции языка"?
У меня нет подтверждающей информации, но есть предчувствие, что транслятор переводит их в те же самые коллекции и итераторы, а уже после этого транслирует их.
После фразы "у меня нет информации, но есть предчувствие", становится понятно, почему вы не можете с этим разобраться. Если что, информация о том, как работает LINQ, открыта.
транслятор переводит их в те же самые коллекции и итераторы, а уже после этого транслирует их.
Нет. LINQ в query-синтаксисе просто переводится в method-синтаксис, а дальше происходит обычный вызов. О каких "коллекциях и итераторах" можно говорить в случае IQueryable
?
А, главное, чем это отличается от foreach
, using
или lock
? Или они теперь тоже не базовые конструкции языка?
К сожалению, здесь Вас огорчу… [...] Конечно же, это не прямой рефлекс собачки Павлова,
Ну вот видите. Собирались огорчить, а на самом деле со мной согласились.
Этим вопросом отдельно не занимался… есть первичные конструкции IList, IQueryable, например.
Ни то, ни другое не является конструкцией языка.
Вы забыли также упомянуть о важных вещах, как IEnumerable, IEnumerator, которые очень важны, когда Вы, например, хотите быстрый JOIN, а не через LINQ.
Когда я хочу быстрый JOIN, я делаю его в БД, и IEnumerable
мне здесь поможет очень слабо.
Но, в любом случае, LINQ по функциональности на голову выше IList, IQueryable.
Это бессмысленное утверждение. LINQ — это набор инструментов, использующих, в том числе, IQueryable
. Не будет IQueryable
— не будет LINQ.
Поэтому это не базовая конструкция языка, а надстройка.
Я только не могу понять, а что же для вас "базовая конструкция языка"?
Но, в любом случае, LINQ — не существовало в момент рождения C#.
В момент рождения C# не существовало даже дженериков. И… что?
в каждом конкретном случае есть только один оптимальный вариант.
Оптимальный по какому набору критериев?
примерно представляю для себя, каким образом это превращается в код.
Именно что "примерно". А потом там проходит JIT, и начинается веселье.
Новые фичи, на мой взгляд, не добавляют функциональности, а лишь в некоторых случаях облегчают читаемость и уменьшают многословность [...] И всё это положено в угоду мелких удобствам
Ну так облегчение читаемости — это не "мелкое удобство".
Представьте мультивложеный LINQ, украшенный многоэтажными лямбдами [...] потом понять [...] слабореально.
Так это не улучшение читаемости, если вы не можете это понять.
К сожалению, когда предлагают нечто удобное, этим сразу же начинают злоупотреблять
… давайте поэтому не предлагать ничего удобного?
Представьте мультивложеный LINQ, украшенный многоэтажными лямбдами…
В процедурном стиле это выглядит ещё хуже и хорошо, если помещается на экран.
А LINQ просто увеличивает плотность кода, из-за чего он действительно читается медленнее.
Можете привести пример такой "жести", когда запрос в linq хуже читается чем в процедурном стиле? Мне вот что-то не приходит в голову.
Зато вот какой пример не придумаешь — лучше выглядит именно в Linq, например:
return Layers.Last().Neurons.OrderByDescending(n => n.Output).First();
(отсюда, возможно этот вопрос ещё не удалён)
… если вы часто делаете join/union, повод задуматься, не взять ли вам другую коллекцию для "быстродействия". Вместо "итераторов".
Хэш-таблицы во всех их вариантах.
В одном из предыдущих каментов я Вам писал, что софт пишу для себя, а не на продажу.
… а что это меняет с точки зрения оптимизации производительности?
Речь идет о том, где его нельзя прописать, там нужно оптимизировать скорость.
Гм. Что значит "нельзя прописать хэш"? Я вас не понимаю. Использование хэш-таблицы вместо другой коллекции — это и есть один из способов "оптимизации скорости".
Надеюсь, мы не трогаем БД?
Нет, не трогаем, потому что тут разговор немедленно сместится в плоскость "LINQ против самописных запросов", а я это терпеть не могу.
Это означает, что я сам решаю, где хэш/C/ASM, а где оставить linq
Я в процессе написания софта, как вы выражаетесь, "на продажу", тоже сам решаю, где какую оптимизацию использовать. Так в чем отличие?
Хэш- далеко не оптимальная оптимизация
А я и не говорил, что она оптимальная. Я сказал, есть повод задуматься, не взять ли ее.
Вы можете вычислять таблицу умножения напрямую, а можете поместить её в хэш.
Какое это имеет отношение к джойнам? Никакого. И да, не надо помещать таблицу умножения в хэш, это заведомо неоптимальная структура, и это понятно из структуры задачи.
Напомните мне — побеседуем отдельно. Слишком объемная тема
… я так понимаю, фраза "я это терпеть не могу" вам не понятна?
Мне что-то интересно стало.
Вот есть два источника данных IEnumerable<Order> orders
и IEnumerable<Customer> customers
. Нужно для каждого ордера вывести его кастомера. Ну то есть, грубо говоря, orders.Join(customers, order => order.CustomerId, customer => customer.Id, someSelector)
. Как вы предлагаете "руками прописать итератор", чтобы было заведомо быстрее, чем дефолтная реализация в LINQ?
Код покажите. Ваше словесное описание мне совершенно не понятно, особенно в части "синхронизировав две коллекции в одну".
Во-первых, это никак не решает задачу Join
, озвученную выше. То есть вообще никак. И никак нельзя перенести принцип.
А во-вторых вы по каждой коллекции проходите дважды, а это в общем случае недопустимо (и в этот момент, извините, ни о какой производительности говорить уже нельзя), хотя для решения вашей задачи это вообще низачем не надо. Подозреваю, кстати, что с учетом этой оговорки Enumerable.Zip(a, b, (a, b) => a+b)
будет быстрее, чем ваше решение.
Это именно join в простейшем виде. Учебный вариант.
Нет. Опишите, пожалуйста, своими словами операцию Join
.
Покажите место, где я прохожу коллекцию дважды. Его там нет
Вот оно:
_a = a.GetEnumerator();
int countA = a.Count();
//...
_a.MoveNext();
Вы в курсе, что делает Enumerable.Count()
?
Ну то есть сначала вы рекомендуете переписывать Join
на собственный итератор, а потом не можете показать пример такого итератора, который бы корректно работал с Join
(и был бы заведомо быстрее версии LINQ).
(вот за это я и не люблю микрооптимизации)
Да, кстати, а почему вы считаете, что ваша версия быстрее вот такой:
IEnumerable<int> MemberwiseSum(IEnumerable<int> a, IEnumerable<int> b)
{
using (IEnumerator<int> ai = a.GetEnumerator())
using (IEnumerator<int> bi = b.GetEnumerator())
while (ai.MoveNext() && ai.MoveNext())
yield return ai.Current+bi.Current;
}
(проверку на равенство длин добавлять лень, она тривиальная, зато в остальном этот код корректнее вашего)
Да, действительно, у меня там опечатка, должно быть
while (ai.MoveNext() && bi.MoveNext())
Но это не объясняет, почему ваш код должен быть быстрее. И тем более не объясняет, как же сделать Join
.
Идея понравилась
… то есть вы, рекомендуя "собственные итераторы", даже не поинтересовались, как работает существующий код?
Неплохо бы поставить Reset перед началом итераций.
Зачем? Там свежий итератор. Более того, итераторы даже не обязаны его поддерживать:
The Reset method is provided for COM interoperability. It does not necessarily need to be implemented; instead, the implementer can simply throw a NotSupportedException.
Ну и да, в вашем коде Reset
на итераторах тоже не вызывается (и правильно), а ваша имплементация Reset
тоже ошибочна — она сбрасывает только счетчик, но не сбрасывает положение в используемых итераторах (еще бы, это невозможно), тем самым приводя к ситуации, когда после вызова Reset
ваш итератор перестает правильно отвечать на MoveNext
.
Использование using — под вопросом.
Никакого вопроса, это шаблонное поведение: использованный до конца итератор должен быть подиспожен, а никакого другого места это сделать у вас нет. То, что в вашем коде Dispose
пустой — заведомая ошибка.
Или вы во внутренности foreach
тоже не смотрели никогда?
Собственно, все вот эти нюансы с Reset
, MoveNext
и Dispose
— это то, почему не надо писать свои итераторы, пока вы можете этого избежать.
Dispose уложит Вашу коллекцию, особенно, если она статическая.
Что значит "уложит"?
(это не говоря о том, что этот код не оперирует "коллекциями", он оперирует последовательностями)
Ну то есть вы давали советы "руками прописывать итераторы, если критические узлы", не разобравшись в том, как работают итераторы в .net.
Ну ок.
Впечатление, что Вы не прочли камент, на который ответили…
Там явно написано " даже не поинтересовался".
Вы, кстати, тоже не поинтересовались
Нет, неверно, потому что код, который я вам привел, основан на существующем.
потому как на Вашу опечатку ai.MoveNext() && ai.MoveNext() компилятор обычно ругается
… вот только этот код никогда не видел компилятора, я его набрал прямо в окне комментария.
Вот я ровно поэтому и пытаюсь от вас добиться: как же конкретно, с примерами кода, вы предлагаете улучшать производительность стандартного Enumerable.Join
с помощью написанных вручную итераторов. Это было бы полезно всем, кто нас читает.
Наверное, у коллекций есть несколько итераторов — первой свежести и не первой.
Нет, зато бывают использованные и неиспользованные итераторы.
так понятно, что по его мнению, в предложенный им модуль коллекции поступают только уже с reset
Вам понятно неправильно. Мне передают IEnumerable
(который, кстати, не коллекция). Он не может быть "с Reset" или "без Reset", он IEnumerable
, у него единственная допустимая операция — GetEnumerator
.
Поэтому необходимо сохранить текущий, сделать reset, а потом восстановить.
Нет, не "необходимо". Контракт IEnumerator
явно говорит, что новый итератор спозиционирован перед началом коллекции, поэтому Reset
бесполезен:
Initially, the enumerator is positioned before the first element in the collection. At this position, Current is undefined. Therefore, you must call MoveNext to advance the enumerator to the first element of the collection before reading the value of Current.
Если Вы пишете универсальную процедуру, то должны предусмотреть, что коллекции на входе находятся не на reset
Нет, не должен. Потому что если переданные мне последовательности соответствуют контракту, то это не нужно, а если они не соответствуют контракту, то я не могу ничего с ними сделать. Поэтому это в любом случае излишняя работа.
Мы априори не знаем, какое положение итератора при передаче коллекции параметром, вернее, знаем — текущее.
При передаче нам последовательности итератора еще не существует. Он возникает в тот момент, когда мы делаем GetEnumerator
. И его положение, согласно контракту — перед первым элементом последовательности на момент вызова. Это то, что нам известно из контракта IEnumerable
/IEnumerator
.
Поэтому его, на всякий случай, лучше сохранить, проделать что хотим и восстановить обратно.
Мы не можем его ни сохранить, ни восстановить. Единственный данный нам гарантированный инструмент — это двигать итератор вперед (на то он и итератор).
Вот вам иллюстрация невозможности: стандартный сгенеренный из yield return
энумератор не поддерживает Reset
вообще. Если вы его вызовете, ваш код упадет.
IEnumerable — интерфейс.
… вот только это не итератор. Это последовательность. А итератор — IEnumerator
.
Когда Вы передаете коллекцию параметром — текущее значение итератора сохраняется.
У коллекции (скажем, у List<T>
) может вообще не быть никакого "текущего значения итератора". А IEnumerable
может быть не только поверх коллекции.
В Вашем фрагменте кода попробуйте передать итератор как параметр — я тоже попробую) и сделать Current
Но зачем мне передавать итератор как параметр, если все стандартные API работают с IEnumerable
? Зачем мне создавать себе дополнительные сложности? Потому и в условии изначальной задачи был IEnumerable
.
Тем временем вот вам еще один пример итератора, который не поддерживает Reset
. А его вы получите, скажем, из File.ReadLines
, системного вызова. И еще один, после которого становится понятно, что от Reset
скорее надо ожидать эксепшн, чем что-либо еще.
Вот только для Join
это принципиально невозможно. Вы точно понимаете, что делает Join
?
Ээээ… вообще не понимаю, что вы имеете в виду.
Вот есть коллекция заказов, в каждом заказе есть идентификатор покупателя, есть рядом коллекция покупателей (с идентификаторами). Что, по вашему, делает операция join по идентификатору покупателя?
Join в данном случае выкладывает одну таблицу, состоящую из данных двух таблиц.
Каким образом сопоставляются данные двух таблиц/коллекций?
Ну банально, на примере. Вот Customers (id: name):
1: Ariane
2: John
3: Andre
Вот Orders (id: customer_id: amount):
1: 3: $50
2: 1: $15
3: 3: $50
4: 2: $25
5: 1: $12
6: 3: $40
И как же они "объединяются"?
Я не знаю, что такое "одномерная табличка". Можете просто результирующие строчки написать?
Если свойство не простой тип, а ссылочный, ссылка с обоих объектов будет на один адрес памяти?
C# обошёл Java в удобстве, вдохновил появление Kotlin, является основным языком для самой популярной среды разработки игр Unity, поддерживается Xamarin'ом, да и WebAssymbly скорее всего массово придёт в наши браузеры через Blazor, потому что всё остальное отстаёт сильнее.
devblogs.microsoft.com/dotnet/performance-improvements-in-net-5
Вот статья, кто еще не видел.
Даже завидно как-то по белому.
Я один тут вижу влияние фанатов Typescript? (P.S я сам веб разработчик на ts и то, куда он сейчас катится, мне не очень нравится) Не так давно смотрел описание RC для Typescript 4.1, там народ тоже с ума сошел в плане добавления новых видов дженериков… Ах, да… Это же одни и те же люди…
Мне лично кажется, что подобными конструкциями для расширения типов будут созданы только проблемы… Из полезного вижу только init'ы и switch шаблоны
Мне вот кажется, что это не от вульгарности отростков, а вот вашего непонимания задач этого кода.
А кто, собственно, сказал, что нынешняя команда C# должна мыслить в ключе ООП?
C# это объектно-ориентированный язык.
Это уже очень давно не чисто объекто-ориентированный язык. Википедия, прямо скажем, честно пишет: "multi-paradigm programming language encompassing static [...] functional, [...] object-oriented (class-based), [...] programming disciplines".
И это, что характерно, к лучшему.
И опыт показывает, что понимание и умение мыслить в ключе ООП явно не достаточно, чтобы getting things done без боли и страданий. Иначе мы бы все сейчас пользовались условной Java 5 или C# 2.0.
Можно теперь написать
foreach (int i in 0..n);
если энумератор к range прикрутить.
Может не быть совместимости, наверное в этом проблема
Не "может не быть", а "точно не будет". Это ровно то, что я написал выше: сумасшедший breaking change.
Мож ещё придумают…
Что придумают? Как сделать это не-breaking change? Вероятность этого пренебрежимо мала, разве что это будет параллельная существующей иерархия типов (то есть, фактически, две системы типов вместо одной, а кому это надо?).
придумали же, например null-значения в ненулевых типах, например bool?
Нет, в типе bool
как не было, так и нет null
. null
есть в Nullable<bool>
, а это совсем другой тип.
Правда, в GetString одни иероглифы…
Не знаю, никогда не видел иероглифов в GetString
(если, конечно, я изначально не положил эти иероглифы в данные). Может быть, дело в том, что у меня "стандартная" англоязычная локаль? Но с nullable-типами это точно никак не связано.
Nullable<bool> t = null;
Console.WriteLine("t: " + t.ToString());
t = true;
Console.WriteLine("t: " + t.ToString());
t = false;
Console.WriteLine("t: " + t.ToString());
дает
t:
t: True
t: False
Никаких иероглифов.
Что нового в C# 9.0