Что нового в C# 9.0

Original author: Mads Torgersen
  • Translation
В преддверии старта нового потока курса «C#-разработчик» представляем вашему вниманию обзор нововведений. Среди них — новый метод доступа к свойству — init, не позволяющий изменять свойства после инициализации, with-выражения для изменения свойств объекта прямо здесь и сейчас, записи и новые возможности сопоставления шаблонов. Подробности, конечно же, под катом.





Это официально: вышел C# 9.0! Еще в мае я написал в блоге о планах C# 9.0, а ниже — обновлённая версия этого поста, соответствующая тому, что мы сделали по факту. С каждой новой версией C# мы стремимся к большей ясности и простоте в обычных ситуациях кодирования, и C# 9.0 — не исключение. На этот раз особое внимание уделяется поддержке краткого и иммутабельного представления форм данных.

Свойства только для инициализации


Инициализаторы объектов весьма удивительны. Они дают клиенту якобы очень гибкий и читаемый формат для создания объекта, а также особенно хороши для создания вложенных объектов, где целое дерево объектов создаётся за один раз. Вот простой пример:

var person = new Person { FirstName = "Mads", LastName = "Torgersen" };

Инициализаторы объектов также освобождают автора типа от написания большого количества шаблонных конструкций – всё, что им нужно сделать — написать какие-то свойства!

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

Единственное большое ограничение на сегодня: для работы инициализаторов объектов свойства должны быть мутабельными: они функционируют, сначала вызывая конструктор объекта (по умолчанию, без параметров в данном случае), а затем присваивая его сеттерам свойств. Свойства только для инициализации исправляют ситуацию! Они вводят метод доступа init, то есть вариант метода доступа set, который может быть вызван только во время инициализации объекта:

public class Person
{
    public string? FirstName { get; init; }
    public string? LastName { get; init; }
}

При таком объявлении приведённый выше клиентский код все еще корректен, но любое последующее присвоение свойств FirstName и LastName — это ошибка:

var person = new Person { FirstName = "Mads", LastName = "Nielsen" }; // OK
person.LastName = "Torgersen"; // ERROR!

Так, свойства только для инициализации защищают состояние объекта от мутаций после завершения инициализации.

Методы доступа init и поля только для чтения


Поскольку методы доступа init могут вызваться только во время инициализации, им разрешено изменять поля заключающего их класса «только для чтения», точно так же, как это можно сделать в конструкторе.

public class Person
{
    private readonly string firstName = "<unknown>";
    private readonly string lastName = "<unknown>";

    public string FirstName 
    { 
        get => firstName; 
        init => firstName = (value ?? throw new ArgumentNullException(nameof(FirstName)));
    }
    public string LastName 
    { 
        get => lastName; 
        init => lastName = (value ?? throw new ArgumentNullException(nameof(LastName)));
    }
}

Записи


В основе классического ООП лежит идея о том, что объект обладает сильной идентичностью и инкапсулирует изменчивое состояние, которое развивается с течением времени. C# всегда отлично работал в этом смысле, но иногда вы хотите почти прямо противоположного, и здесь значения по умолчанию C#, как правило, мешают, делая работу крайне трудоёмкой. Если вы хотите, чтобы весь объект был неизменяемым и вел себя как значение, вам следует рассмотреть возможность его объявления как record:

public record Person
{
    public string? FirstName { get; init; }
    public string? LastName { get; init; }
}

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

With-выражения


При работе с неизменяемыми данными общий шаблон — создание новых значений из существующих для представления нового состояния. Например, если бы наш человек изменил фамилию, мы представили бы его как новый объект — копию старого, за исключением фамилии. Этот метод часто называют «недеструктивной мутацией». Вместо того чтобы представлять человека через какое-то время, запись представляет состояние персоны в данный момент. Чтобы поддержать этот стиль программирования, записи допускают новый вид выражений: with-выражения:

var person = new Person { FirstName = "Mads", LastName = "Nielsen" };
var otherPerson = person with { LastName = "Torgersen" };

With-выражения используют синтаксис инициализатора объекта, чтобы указать, что именно отличается в новом объекте от старого объекта. Можно указать несколько свойств. Это выражение работает, фактически копируя полное состояние старого объекта в новое, а затем мутируя его в соответствии с инициализатором объекта. Это означает, что свойства должны иметь метод доступа init или set, затем изменяемый в with-выражении.

Равенство на основе значения


Все объекты наследуют виртуальный метод Equals(object) класса object. Это поведение используется как основа для статического метода Object.Equals(object, object), когда оба параметра ненулевые. Структуры переопределяют этот метод, чтобы иметь «равенство на основе значений», сравнивая каждое поле структуры и рекурсивно вызывая для полей «Equals». Записи делают то же самое. Это означает, что в соответствии с их «значением» две записи могут быть равны друг другу, не будучи одним и тем же объектом. Например, если мы снова изменим фамилию в Person:

var originalPerson = otherPerson with { LastName = "Nielsen" };

Теперь у нас будет ReferenceEquals(person, originalPerson) — ложь (это не один и тот же объект), но Equals(person, originalPerson) — истина (они имеют одинаковое значение). Наряду с основанным на значении Equals, есть также основанное на значении переопределение GetHashCode(), которое будет работать вместе с ним. Кроме того, записи реализуют интерфейс IEquatable <T> и перегружают операторы == и !=, Так что поведение, основанное на значениях, последовательно проявляется во всех этих различных механизмах равенства.

Равенство значений и мутабельность не всегда хорошо сочетаются. Одна из проблем заключается в том, что изменение значений может привести с течением времени к изменению результата GetHashCode(), что неудачно, когда объект хранится в хеш-таблице! Мы не запрещаем мутабельные записи, но и не поощряем их, если вы не продумали последствия!

Наследование


Записи могут наследовать от других записей:

public record Student : Person
{
    public int ID;
}

With-выражения и равенство значений хорошо работают вместе с наследованием записей, поскольку они учитывают весь объект среды выполнения, а не только тип, под которым объект известен статически. Скажем, я создаю Student, но сохраняю его в переменной Person:

Person student = new Student { FirstName = "Mads", LastName = "Nielsen", ID = 129 };

Выражение with по-прежнему будет копировать весь объект с сохранением типа среды выполнения:

var otherStudent = student with { LastName = "Torgersen" };
WriteLine(otherStudent is Student); // true

Таким же образом равенство значений гарантирует, что два объекта имеют один и тот же тип среды выполнения, а затем сравнивает все их состояния:

Person similarStudent = new Student { FirstName = "Mads", LastName = "Nielsen", ID = 130 };
WriteLine(student != similarStudent); //true, since ID's are different

Позиционные записи


Иногда полезно иметь более позиционный подход к записи, при котором её содержимое задается с помощью аргументов конструктора и может извлекаться с помощью позиционной деконструкции. Вполне возможно в записи указать свой собственный конструктор и деконструктор:

public record Person 
{ 
    public string FirstName { get; init; } 
    public string LastName { get; init; }
    public Person(string firstName, string lastName) 
      => (FirstName, LastName) = (firstName, lastName);
    public void Deconstruct(out string firstName, out string lastName) 
      => (firstName, lastName) = (FirstName, LastName);
}

Но существует гораздо более короткий синтаксис для выражения точно того же:

public record Person(string FirstName, string LastName);

Код объявляет только инициализирующие открытые авто-свойства и конструктор, и деконструктор, так что вы можете написать:

var person = new Person("Mads", "Torgersen"); // positional construction
var (f, l) = person;                        // positional deconstruction

Если вам не нравится автоматически сгенерированное свойство, вы можете определить свое собственное свойство с тем же именем, и сгенерированный конструктор и деконструктор просто будут использовать его. В этом случае параметр находится в области действия, которую можно использовать для инициализации. Скажем, например, вы предпочитаете, чтобы FirstName было защищенным свойством:

public record Person(string FirstName, string LastName)
{
    protected string FirstName { get; init; } = FirstName; 
}

Позиционная запись может вызывать базовый конструктор следующим образом:

public record Student(string FirstName, string LastName, int ID) : Person(FirstName, LastName);

Программы верхнего уровня


Написание простой программы на C# требует значительного количества шаблонного кода:

using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}

Это не только ошеломляет новичков в языке, но и загромождает код, добавляя уровни отступов. В C# 9.0 вы можете написать программу на самом верхнем уровне, вот так:

using System;

Console.WriteLine("Hello World!");

Допускается любое выражение. Программа должна выполняться после «using» и перед любыми объявлениями типа или пространства имен в файле, и это возможно только в одном файле, точно так же, как на данный момент у вас может быть только один метод Main. Допустимы return и await. А если вы хотите получить доступ к аргументам командной строки, то args доступен как «магический» параметр.

using static System.Console;
using System.Threading.Tasks;

WriteLine(args[0]);
await Task.Delay(1000);
return 0;

Локальные функции — это форма выражений, они также разрешены в программе верхнего уровня. Ошибкой будет вызывать их из любого места за пределами раздела выражений верхнего уровня.

Улучшено сопоставление шаблонов


В C# 9.0 добавлено несколько новых типов шаблонов. давайте рассмотрим их в контексте этого фрагмента кода из туториала по сопоставлению шаблонов:

public static decimal CalculateToll(object vehicle) =>
    vehicle switch
    {
       ...

        DeliveryTruck t when t.GrossWeightClass > 5000 => 10.00m + 5.00m,
        DeliveryTruck t when t.GrossWeightClass < 3000 => 10.00m - 2.00m,
        DeliveryTruck _ => 10.00m,

        _ => throw new ArgumentException("Not a known vehicle type", nameof(vehicle))
    };

Простой шаблон типа


Сейчас, когда тип совпадает, шаблон типа должен объявлять идентификатор – даже если это пустой идентификатор _, как в DeliveryTruck _ выше. Но в C# 9.0 можно просто написать тип:

DeliveryTruck => 10.00m,

Шаблоны отношений


В C# 9.0 введены шаблоны, соответствующие операторам отношений <, <= и так далее. Так что теперь вы можете записать часть DeliveryTruck вышеуказанного шаблона как вложенное выражение switch:

DeliveryTruck t when t.GrossWeightClass switch
{
    > 5000 => 10.00m + 5.00m,
    < 3000 => 10.00m - 2.00m,
    _ => 10.00m,
},

Здесь > 5000 и < 3000 — шаблон отношений.

Логические шаблоны


Наконец, вы можете комбинировать шаблоны с логическими операторами and, or и not, записанными в виде слов, чтобы избежать путаницы с операторами в выражениях. Например, случаи вложенного switch выше можно расположить в порядке возрастания вот так:

DeliveryTruck t when t.GrossWeightClass switch
{
    < 3000 => 10.00m - 2.00m,
    >= 3000 and <= 5000 => 10.00m,
    > 5000 => 10.00m + 5.00m,
},

В середине and используется для объединения двух реляционных паттернов и формирования паттерна, представляющего интервал. Обычно шаблон not применяется к шаблону констант null, как в not null. Например, мы можем разделить обработку неизвестных случаев в зависимости от того, являются ли они null:

not null => throw new ArgumentException($"Not a known vehicle type: {vehicle}", nameof(vehicle)),
null => throw new ArgumentNullException(nameof(vehicle))

Также not удобен в содержащих is-выражения условиях if вместо громоздких двойных скобок:

if (!(e is Customer)) { ... }

Можно написать только это:

if (e is not Customer) { ... }

Фактически в таком выражении is not мы позволяем вам как-то назвать Customer для дальнейшего использования:

if (e is not Customer c) { throw ... } // if this branch throws or returns...
var n = c.FirstName; // ... c is definitely assigned here

Выражения new и целевой тип


«Целевой тип» — это термин, который мы употребляем, когда выражение получает свой тип из контекста того места, где оно используется. Например, null и лямбда-выражения всегда целевые. Выражения new в C# всегда требовали явного указания типа (кроме неявно типизированных выражений массива). В C # 9.0 можно не указывать тип, если есть чёткий тип, которому присваивается выражение.

Point p = new (3, 5);

Это особенно удобно, когда у вас много повторений, например, в массиве или инициализаторе объекта:

Point[] ps = { new (1, 2), new (5, 2), new (5, -3), new (1, -3) }; 

Ковариантные return


Иногда полезно указать, что переопределение метода в производном классе имеет более конкретный возвращаемый тип, чем объявление в базовом типе. C# 9.0 позволяет это сделать:

abstract class Animal
{
    public abstract Food GetFood();
    ...
}
class Tiger : Animal
{
    public override Meat GetFood() => ...;
}

И многое другое


Узнать о полном наборе функций C# 9.0 можно в документации «Что нового в C# 9.0».
А с промокодом HABR можно получить дополнительные 10 % к скидке, указанной на баннере.

image



Рекомендуемые статьи


SkillFactory
Школа Computer Science. Скидка 10% по коду HABR

Comments 167

    +2
    Дежавю
      +12

      Классическая статья с "актуальным" материалом (на самом деле ради рекламы).

      0
      «With-выражения» — теперь всякие клонаторы не нужны? Хотя надо еще посмотреть как оно с deep clone работает.
        0

        Никак оно с deep clone не работает. Или вы хотите работать с record-ами, в которые некоторые поля указывают на изменяемые объекты?

          0
          Глубокого клонирования не будет в любом случае. Поверхностное — да.
        • UFO just landed and posted this here
          • UFO just landed and posted this here
              +7

              Ну вот кто-то счёл, что ваш комент образец трэша и неадеквата и намекнул вам об этом минусом.

              • UFO just landed and posted this here
                  +4

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

                  • UFO just landed and posted this here
                    • UFO just landed and posted this here
                  +1
                  И если создатели плывут в сторону от строгой объектно-ориентированности

                  … которой там никогда не было.


                  Ну ладно, ок, первые версии еще ничего. Но как только у вас появились inline delegates, точно стало понятно, что это язык со смешанной парадигмой. А LINQ — следующий шаг в этом направлении. И дальше, дальше, дальше.


                  Ну не нравится вам, ок, не нравится. Вы пишете, что вам не нравится. Кому-то не нравится ваш комментарий, и они выражают свое отношение к вашему комментарию тем способом, который на этом ресурсе принят.

                    +8
                    Минусы без аргументации почему — это ничто.

                    Минус без аргументации обычно означает «я искренне считаю, что вы написали хрень, но диалог с вами мне не настолько интересен, чтобы раскрывать подробно свою мысль». Вполне себе адекватный ответ.
                    ресурс без треша и неадеквата.

                    Ну, вы не перегибайте так.
                    • UFO just landed and posted this here
                        +1
                        Например, в описанной Вами ситуации многие не считают разумным тратить время и усилия почитать

                        Те, кто не читает, вообще никакого мнения про ваш текст не имеют, поэтому вполне логично, что ни минусов, ни плюсов они не ставят :) Я имею в виду тех, кто читает. Мне, например, тема С# 9 интересна, поэтому я прочитал и статью, и комментарии под ней. Ваш комментарий мне тоже абсолютно честно показался каким-то нелепым. Минусы я ставить не могу, да и не стал бы даже если бы мог, т.к. мне это просто не важно, но прекрасно понимаю тех, кто ставит.
                        • UFO just landed and posted this here
                        0
                        Минус без аргументации обычно означает «я искренне считаю, что вы написали хрень, но диалог с вами мне не настолько интересен, чтобы раскрывать подробно свою мысль»


                        критика должна быть конструктивной, иначе какой в ней смысл
                          0

                          Вообще-то, у критики бывают разные задачи, и не все из них требуют конструктива.

                            0
                            тогда это не критика, а личное суждение
                              0

                              А почему вы противопоставляете критику личному суждению? Это не обязательно взаимоисключающие вещи.


                              А главное, сверху даже критики-то не обещал никто.

                                0
                                Ну потому что личое суждение, например — «ты дурак»
                                Конструктивная критика — «я не согласен с вашим мнением, т.к. вы не учли такие-то и такие-то моменты»
                                Первое никому ничего не даст, со второго можно чему-то научиться

                                  0
                                  Конструктивная критика — «я не согласен с вашим мнением, т.к. вы не учли такие-то и такие-то моменты»

                                  Вот только она при этом все еще может остаться личным суждением. Например, потому что эти моменты важны только по личному суждению критикующего.


                                  Первое никому ничего не даст, со второго можно чему-то научиться

                                  А почему вы решили, что стоит задача кого-то чему-то научить?

                                    0
                                    Вот только она при этом все еще может остаться личным суждением


                                    я не очень понимаю, как
                                    если мы спорим про программирование, и я, например, говорю что у линковского .find сложность О(log(n)) и поэтому на небольших коллекциях им искать — самое оно, как это может быть личным суждением? Оно ошибочное, и доказать это — минута гугления. Мой оппонент будет прав, даже если лично мне он неприятен

                                    А почему вы решили, что стоит задача кого-то чему-то научить?


                                    или научиться
                                    А иначе зачем ввязываться в дискуссии в интернете? Я могу понять в личном общении, там и поорать можно, и в рыло оппоненту просунуть (или получить), жизнь как есть
                                    А в интернете-то…
                                      0
                                      если мы спорим про программирование, и я, например, говорю что у линковского .find сложность О(log(n)) и поэтому на небольших коллекциях им искать — самое оно, как это может быть личным суждением?

                                      "Самое оно" — это и есть личное суждение.


                                      Оно ошибочное, и доказать это — минута гугления.

                                      Неа, нельзя доказать, что метод x на небольших коллекциях не самое оно. Можно доказать, что у него другая сложность.


                                      А иначе зачем ввязываться в дискуссии в интернете?

                                      Напомню, что началось все с того, что человек поставил минус именно потому, что он не хотел ввязываться в дискуссию.

                                        0
                                        если мы спорим про программирование, и я, например, говорю что у линковского .find сложность О(log(n)) и поэтому на небольших коллекциях им искать — самое оно, как это может быть личным суждением?

                                        «Самое оно» — это и есть личное суждение.


                                        Его элементарно можно опровергнуть; а чье-то личное мнение попробуйте опровергнуть, ха

                                        Напомню, что началось все с того, что человек поставил минус именно потому, что он не хотел ввязываться в дискуссию.


                                        ну так оно и выглядит как «сам дурак»
                                          0
                                          Его элементарно можно опровергнуть

                                          Попробуйте.


                                          ну так оно и выглядит как «сам дурак»

                                          Но свою задачу прекрасно выполняет. О чем, собственно, и речь.

                                            0
                                            Попробуйте.

                                            docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.find?view=net-5.0
                                            Исходное предположение неверно, эрго, последующий вывод — тоже
                                            Но свою задачу прекрасно выполняет


                                            Какую?
                                              +1
                                              Исходное предположение неверно, эрго, последующий вывод — тоже

                                              Nope. "Я считаю", что на небольших коллекциях сложность не имеет значения, поэтому этот метод — самое оно.


                                              Какую?

                                              Продемонстрировать отношение к высказанному в комментарии тезису.

                                                0
                                                Nope. «Я считаю», что на небольших коллекциях сложность не имеет значения, поэтому этот метод — самое оно.


                                                ну так это совсем другое исходное предположение, никакого отношения к моему не имеет; соответственно, весь наш спор бессмысленнен

                                                Продемонстрировать отношение к высказанному в комментарии тезису.


                                                т.к. нет никакой обратной связи, это интересно только продемонстрировавшему
                                                  0
                                                  ну так это совсем другое исходное предположение, никакого отношения к моему не имеет; соответственно, весь наш спор бессмысленнен

                                                  Так и ваш исходный пример не имеет никакого отношения к критике, выраженной в виде личного мнения, так что все логично.


                                                  т.к. нет никакой обратной связи, это интересно только продемонстрировавшему

                                                  Обратная связь как раз есть: рейтинг комментария. И он интересен не только написавшим, но и остальным. Я вот смотрю на рейтинги.

                                                    0
                                                    Так и ваш исходный пример не имеет никакого отношения к критике, выраженной в виде личного мнения, так что все логично.


                                                    не, критика в виде личного мнения была бы КГ/АМ, а не ссылки на страницы документации

                                                    Обратная связь как раз есть: рейтинг комментария.


                                                    Он ничего не выражает и не обьясняет
                                                    Может, человек просто не с той ноги встал. Может, он просто не в теме (см. эффект Даннинга-Крюгера). Может, он и имел что-то конкретное в виду, но мы этого не узнаем
                                                    А мне бы хотелось не полагаться просто на чье-то мнение, а видеть аргументы сторон, чтоб я сам мог принять решение
                                                      0
                                                      не, критика в виде личного мнения была бы КГ/АМ, а не ссылки на страницы документации

                                                      Вооот. Это типичный пример личного суждения. Вы считаете, что критика в виде личного суждения была бы [...]. Это, заметим, ваша критика чужого поведения. Иными словами, критика, которую вы даете, не соответствует вашим же требованиям к критике, и при этом подтверждает мой тезис.


                                                      Повторюсь еще раз: возможна критика, являющаяся личным суждением, но при этом не сводящаяся к КГ/АМ.


                                                      Он ничего не выражает и не обьясняет

                                                      Отнюдь. Он выражает некую совокупную реакцию читателей на комментарий. А объяснять он ничего и не должен.


                                                      А мне бы хотелось не полагаться просто на чье-то мнение, а видеть аргументы сторон, чтоб я сам мог принять решение

                                                      Вам бы хотелось — это ваши личные пожелания, не правда ли?

                                                        0
                                                        Вы считаете, что критика в виде личного суждения была бы


                                                        Кри́тика (от фр. critique из др.-греч. κριτική τέχνη «искусство разбирать, суждение») — анализ, оценка о явлениях какой-либо области человеческой деятельности[1]. Задачами критики являются:

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


                                                          0

                                                          Так вот, личное суждение "это плохо" вполне является оценкой. Все прекрасно совпало.

                                                            0
                                                            такую оценку может дать даже тетя Циля с Привоза
                                                            ценность ее будет невелика
                                                              0

                                                              Для вас — возможно. Для кого-то другого — возможно, нет.


                                                              А главное, от того, что для вас эта оценка не ценна, она не перестает быть критикой (если, конечно, это замышлялось как критика).

                        0
                        И мне удивительно, что меня минусуют прям все, кому не лень.

                        Неуместная метафора — вы преувеличиваете.


                        Минусы без аргументации почему — это ничто.

                        А плюсы?

                        • UFO just landed and posted this here
                            +1
                            Может быть, я и не прав, но, когда плюсуют — это согласие и поддержка твоего поста.
                            Но вот если минусуют…

                            А если минусуют — несогласие. Очень логично.

                      • UFO just landed and posted this here
                          0
                          Шлю луч поддержки! Именно так и есть. Строгий красивый язык снабжают желеобразными конструкциями. Я не доволен. В какой-то момент изящный простой топор начал обретать двойные ручки, выпирающие крючки, защёлки-фиксаторы… и встроенный будильник.
                          Всё это вырождает не язык, нет! Это порождает вольность (в плохом смысле этого слова) в командной работе над проектами. Когда один разработчик пишет код в классическом стиле, а другой плодит в коде эти новомодные чудовища. Как вести такой проект?
                            0

                            Проектный или командный code style convention, желательно с проверкой лиентервми.

                              0
                              Вот! Хорошо, если команда устоявшаяся или этот самый convention есть кому сформулировать. А когда чего-то из этого нет, могут начаться проблемы и тут винить некого по многим причинам.
                                +3

                                … а когда чего-то из этого нет, вам язык не поможет все равно. Так что не в языке проблема, нет.

                                  0
                                  Как минимум, часть проекта не будет изувечена новой стилистикой.
                                    +2

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

                                      0
                                      Согласен! И каждое сомнительное нововведение будет уводить от строгой дисциплины, как снежный ком. Красота любой игры в строгих правилах: шахматы, футбол. Я думаю, Вы должны понимать о чём речь.
                                        +1
                                        И каждое сомнительное нововведение будет уводить от строгой дисциплины, как снежный ком.

                                        Не было там никогда "строгой дисциплины", поэтому и уводить неоткуда.


                                        Красота любой игры в строгих правилах: шахматы, футбол.

                                        Я не знаю про "любую игру", но я знаю, что красота искусства в том, чтобы в нужный момент полностью проигнорировать правила.


                                        Но код — он и не игра, и не искусство, он просто средство достижения цели. Слишком строгие правила усложняют достижение цели, а не способствуют ему.

                                        0

                                        Можете подробнее раскрыть, чем вам не нравятся лямбда-выражения, что вы считаете, что их использование изувечит код?

                                          0

                                          Мне они как раз (почти) всем нравятся. Я просто продолжаю логику исходного комментария о "новомодных чудовищах".

                                            0

                                            Понял. Мне тоже нравятся, вот я и подумал, что ж с ними не так-то… Спасибо за уточнение!

                              • UFO just landed and posted this here
                                  +1
                                  И у меня есть предчувствие, что эффективность кода при этом снижается, тяжеловесность растёт.

                                  Не знаю как в мире C#, но в мире PHP, например, принято при выходе новых синтаксических и подобных фич сравнивать не только код "было"-"стало", но бенчмарки прогонять.


                                  А в наличии только одного оптимального варианта я очень сомневаюсь. Хотя бы потому что метрики оптимальности/эффективности могут быть разными: тут нам важны циклы процессора, тут потребляемая память, тут отсутствие блокировок, а тут гарантии времени выполнения.


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

                                  • UFO just landed and posted this here
                                      0
                                      Поэтому, вне зависимости от ОС и процессора можно ±спрогнозировать эффективность кода.

                                      … и выяснить, что прогноз бесконечно далек от реальности, потому что вы не учли работу рантайма.

                                      • UFO just landed and posted this here
                                          0
                                          Другое дело, если Вы пишете конкретный проект под конкретного заказчика.

                                          Ну вот я не пишу конкретный проект под конкретного заказчика. Я даже версию Windows, на которой будет код выполняться, не всегда точно знаю.


                                          В этом случае гораздо проще оценить предполагаемое быстродействие, например.

                                          Оценить его и сейчас можно, в этом отношении ничего не изменилось.

                                          • UFO just landed and posted this here
                                              0

                                              Как это связано с тем, что я сказал?

                                              • UFO just landed and posted this here
                                                  0
                                                  Из двух заданных Вами вопросов, я отвечал именно на этот.

                                                  Я вроде бы не задавал никакого вопроса.


                                                  Если Вы пишете ± универсал-проект -рекомендовал бы протестить на узкие места, которые видны на любом компе.

                                                  … и почему вы думаете, что это не сделали?

                                                  • UFO just landed and posted this here
                                    +1
                                    облегчают читаемость и уменьшают многословность

                                    Программисты 90% времени читают код, лишь 10% пишут, поэтому мне кажется, что зря настолько легко отмахиваетесь от аргумента "основная работа становится проще".

                                    • UFO just landed and posted this here
                                        +2
                                        А если ещё дедлайн. Безусловно, в этом случае новые фичи — ценная вещь.

                                        Ровно наоборот. Если у вас дедлайн, хвататься за новый фичи можно только в том случае, если без них никак, во всех остальных случаях надо сидеть на том, что уже известно.


                                        а это именно надстройки над языком

                                        Да нет, это сам язык и есть. Как вы отличаете "язык" от "надстроек"?

                                        • UFO just landed and posted this here
                                            +1
                                            Конечно, сорри, но мы как собаки Павлова…

                                            Нет, мы не как собаки Павлова.


                                            Есть классика, базовые конструкции языка.

                                            Что такое "базовые конструкции языка"?


                                            У меня нет подтверждающей информации, но есть предчувствие, что транслятор переводит их в те же самые коллекции и итераторы, а уже после этого транслирует их.

                                            После фразы "у меня нет информации, но есть предчувствие", становится понятно, почему вы не можете с этим разобраться. Если что, информация о том, как работает LINQ, открыта.


                                            транслятор переводит их в те же самые коллекции и итераторы, а уже после этого транслирует их.

                                            Нет. LINQ в query-синтаксисе просто переводится в method-синтаксис, а дальше происходит обычный вызов. О каких "коллекциях и итераторах" можно говорить в случае IQueryable?


                                            А, главное, чем это отличается от foreach, using или lock? Или они теперь тоже не базовые конструкции языка?

                                            • UFO just landed and posted this here
                                                +1
                                                К сожалению, здесь Вас огорчу… [...] Конечно же, это не прямой рефлекс собачки Павлова,

                                                Ну вот видите. Собирались огорчить, а на самом деле со мной согласились.


                                                Этим вопросом отдельно не занимался… есть первичные конструкции IList, IQueryable, например.

                                                Ни то, ни другое не является конструкцией языка.


                                                Вы забыли также упомянуть о важных вещах, как IEnumerable, IEnumerator, которые очень важны, когда Вы, например, хотите быстрый JOIN, а не через LINQ.

                                                Когда я хочу быстрый JOIN, я делаю его в БД, и IEnumerable мне здесь поможет очень слабо.


                                                Но, в любом случае, LINQ по функциональности на голову выше IList, IQueryable.

                                                Это бессмысленное утверждение. LINQ — это набор инструментов, использующих, в том числе, IQueryable. Не будет IQueryable — не будет LINQ.


                                                Поэтому это не базовая конструкция языка, а надстройка.

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


                                                Но, в любом случае, LINQ — не существовало в момент рождения C#.

                                                В момент рождения C# не существовало даже дженериков. И… что?

                                                • UFO just landed and posted this here
                                      +2
                                      в каждом конкретном случае есть только один оптимальный вариант.

                                      Оптимальный по какому набору критериев?


                                      примерно представляю для себя, каким образом это превращается в код.

                                      Именно что "примерно". А потом там проходит JIT, и начинается веселье.


                                      Новые фичи, на мой взгляд, не добавляют функциональности, а лишь в некоторых случаях облегчают читаемость и уменьшают многословность [...] И всё это положено в угоду мелких удобствам

                                      Ну так облегчение читаемости — это не "мелкое удобство".

                                      • UFO just landed and posted this here
                                          +1
                                          Представьте мультивложеный LINQ, украшенный многоэтажными лямбдами [...] потом понять [...] слабореально.

                                          Так это не улучшение читаемости, если вы не можете это понять.


                                          К сожалению, когда предлагают нечто удобное, этим сразу же начинают злоупотреблять

                                          … давайте поэтому не предлагать ничего удобного?

                                            +1
                                            Представьте мультивложеный LINQ, украшенный многоэтажными лямбдами…

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

                                            • UFO just landed and posted this here
                                                0

                                                Можете привести пример такой "жести", когда запрос в linq хуже читается чем в процедурном стиле? Мне вот что-то не приходит в голову.


                                                Зато вот какой пример не придумаешь — лучше выглядит именно в Linq, например:


                                                return Layers.Last().Neurons.OrderByDescending(n => n.Output).First();

                                                (отсюда, возможно этот вопрос ещё не удалён)

                                                • UFO just landed and posted this here
                                                  • UFO just landed and posted this here
                                                      0

                                                      … если вы часто делаете join/union, повод задуматься, не взять ли вам другую коллекцию для "быстродействия". Вместо "итераторов".

                                                      • UFO just landed and posted this here
                                                          0

                                                          Хэш-таблицы во всех их вариантах.

                                                          • UFO just landed and posted this here
                                                              0
                                                              В одном из предыдущих каментов я Вам писал, что софт пишу для себя, а не на продажу.

                                                              … а что это меняет с точки зрения оптимизации производительности?


                                                              Речь идет о том, где его нельзя прописать, там нужно оптимизировать скорость.

                                                              Гм. Что значит "нельзя прописать хэш"? Я вас не понимаю. Использование хэш-таблицы вместо другой коллекции — это и есть один из способов "оптимизации скорости".


                                                              Надеюсь, мы не трогаем БД?

                                                              Нет, не трогаем, потому что тут разговор немедленно сместится в плоскость "LINQ против самописных запросов", а я это терпеть не могу.

                                                              • UFO just landed and posted this here
                                                                  0
                                                                  Это означает, что я сам решаю, где хэш/C/ASM, а где оставить linq

                                                                  Я в процессе написания софта, как вы выражаетесь, "на продажу", тоже сам решаю, где какую оптимизацию использовать. Так в чем отличие?


                                                                  Хэш- далеко не оптимальная оптимизация

                                                                  А я и не говорил, что она оптимальная. Я сказал, есть повод задуматься, не взять ли ее.


                                                                  Вы можете вычислять таблицу умножения напрямую, а можете поместить её в хэш.

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


                                                                  Напомните мне — побеседуем отдельно. Слишком объемная тема

                                                                  … я так понимаю, фраза "я это терпеть не могу" вам не понятна?

                                                        0

                                                        Мне что-то интересно стало.


                                                        Вот есть два источника данных IEnumerable<Order> orders и IEnumerable<Customer> customers. Нужно для каждого ордера вывести его кастомера. Ну то есть, грубо говоря, orders.Join(customers, order => order.CustomerId, customer => customer.Id, someSelector). Как вы предлагаете "руками прописать итератор", чтобы было заведомо быстрее, чем дефолтная реализация в LINQ?

                                                        • UFO just landed and posted this here
                                                            0

                                                            Код покажите. Ваше словесное описание мне совершенно не понятно, особенно в части "синхронизировав две коллекции в одну".

                                                            • UFO just landed and posted this here
                                                                +2

                                                                Во-первых, это никак не решает задачу Join, озвученную выше. То есть вообще никак. И никак нельзя перенести принцип.


                                                                А во-вторых вы по каждой коллекции проходите дважды, а это в общем случае недопустимо (и в этот момент, извините, ни о какой производительности говорить уже нельзя), хотя для решения вашей задачи это вообще низачем не надо. Подозреваю, кстати, что с учетом этой оговорки Enumerable.Zip(a, b, (a, b) => a+b) будет быстрее, чем ваше решение.

                                                                • UFO just landed and posted this here
                                                                    +1
                                                                    Это именно join в простейшем виде. Учебный вариант.

                                                                    Нет. Опишите, пожалуйста, своими словами операцию Join.


                                                                    Покажите место, где я прохожу коллекцию дважды. Его там нет

                                                                    Вот оно:


                                                                    _a = a.GetEnumerator();
                                                                    int countA = a.Count();
                                                                    //...
                                                                    _a.MoveNext();

                                                                    Вы в курсе, что делает Enumerable.Count()?

                                                                    • UFO just landed and posted this here
                                                                        0

                                                                        Ну то есть сначала вы рекомендуете переписывать 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;
                                                                        }

                                                                        (проверку на равенство длин добавлять лень, она тривиальная, зато в остальном этот код корректнее вашего)

                                                                        • UFO just landed and posted this here
                                                                            0

                                                                            Да, действительно, у меня там опечатка, должно быть


                                                                            while (ai.MoveNext() && bi.MoveNext())

                                                                            Но это не объясняет, почему ваш код должен быть быстрее. И тем более не объясняет, как же сделать Join.

                                                                            • UFO just landed and posted this here
                                                                                0
                                                                                Идея понравилась

                                                                                … то есть вы, рекомендуя "собственные итераторы", даже не поинтересовались, как работает существующий код?


                                                                                Неплохо бы поставить 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.

                                                                                (https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerator-1?view=net-5.0#remarks)


                                                                                Ну и да, в вашем коде Reset на итераторах тоже не вызывается (и правильно), а ваша имплементация Reset тоже ошибочна — она сбрасывает только счетчик, но не сбрасывает положение в используемых итераторах (еще бы, это невозможно), тем самым приводя к ситуации, когда после вызова Reset ваш итератор перестает правильно отвечать на MoveNext.


                                                                                Использование using — под вопросом.

                                                                                Никакого вопроса, это шаблонное поведение: использованный до конца итератор должен быть подиспожен, а никакого другого места это сделать у вас нет. То, что в вашем коде Dispose пустой — заведомая ошибка.


                                                                                Или вы во внутренности foreach тоже не смотрели никогда?


                                                                                Собственно, все вот эти нюансы с Reset, MoveNext и Dispose — это то, почему не надо писать свои итераторы, пока вы можете этого избежать.


                                                                                Dispose уложит Вашу коллекцию, особенно, если она статическая.

                                                                                Что значит "уложит"?


                                                                                (это не говоря о том, что этот код не оперирует "коллекциями", он оперирует последовательностями)

                                                                                • UFO just landed and posted this here
                                                                                    0

                                                                                    Ну то есть вы давали советы "руками прописывать итераторы, если критические узлы", не разобравшись в том, как работают итераторы в .net.


                                                                                    Ну ок.

                                                                                    • UFO just landed and posted this here
                                                                                        0
                                                                                        Впечатление, что Вы не прочли камент, на который ответили…

                                                                                        Там явно написано " даже не поинтересовался".

                                                                                        • UFO just landed and posted this here
                                                                                            0
                                                                                            Вы, кстати, тоже не поинтересовались

                                                                                            Нет, неверно, потому что код, который я вам привел, основан на существующем.


                                                                                            потому как на Вашу опечатку ai.MoveNext() && ai.MoveNext() компилятор обычно ругается

                                                                                            … вот только этот код никогда не видел компилятора, я его набрал прямо в окне комментария.

                                                                                            • UFO just landed and posted this here
                                                                                              0
                                                                                              Сфигали?
                                                                                              Пруф
                                                                                              Возвращает каждый второй элемент коллекции
                                                                                              • UFO just landed and posted this here
                                                                                                • UFO just landed and posted this here
                                                                                                    0

                                                                                                    Вот я ровно поэтому и пытаюсь от вас добиться: как же конкретно, с примерами кода, вы предлагаете улучшать производительность стандартного Enumerable.Join с помощью написанных вручную итераторов. Это было бы полезно всем, кто нас читает.

                                                                                                    • UFO just landed and posted this here
                                                                                                        +2

                                                                                                        … вот только в личку не надо, потому что мы говорим о пользе для всех читающих.

                                                                                                    +1
                                                                                                    Наверное, у коллекций есть несколько итераторов — первой свежести и не первой.

                                                                                                    Нет, зато бывают использованные и неиспользованные итераторы.


                                                                                                    так понятно, что по его мнению, в предложенный им модуль коллекции поступают только уже с 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.
                                                                                                    • UFO just landed and posted this here
                                                                                                        +3
                                                                                                        Если Вы пишете универсальную процедуру, то должны предусмотреть, что коллекции на входе находятся не на reset

                                                                                                        Нет, не должен. Потому что если переданные мне последовательности соответствуют контракту, то это не нужно, а если они не соответствуют контракту, то я не могу ничего с ними сделать. Поэтому это в любом случае излишняя работа.

                                                                                                        • UFO just landed and posted this here
                                                                                                            +2
                                                                                                            Мы априори не знаем, какое положение итератора при передаче коллекции параметром, вернее, знаем — текущее.

                                                                                                            При передаче нам последовательности итератора еще не существует. Он возникает в тот момент, когда мы делаем GetEnumerator. И его положение, согласно контракту — перед первым элементом последовательности на момент вызова. Это то, что нам известно из контракта IEnumerable/IEnumerator.


                                                                                                            Поэтому его, на всякий случай, лучше сохранить, проделать что хотим и восстановить обратно.

                                                                                                            Мы не можем его ни сохранить, ни восстановить. Единственный данный нам гарантированный инструмент — это двигать итератор вперед (на то он и итератор).


                                                                                                            Вот вам иллюстрация невозможности: стандартный сгенеренный из yield return энумератор не поддерживает Reset вообще. Если вы его вызовете, ваш код упадет.

                                                                                                            • UFO just landed and posted this here
                                                                                                                +1
                                                                                                                IEnumerable — интерфейс.

                                                                                                                … вот только это не итератор. Это последовательность. А итератор — IEnumerator.


                                                                                                                Когда Вы передаете коллекцию параметром — текущее значение итератора сохраняется.

                                                                                                                У коллекции (скажем, у List<T>) может вообще не быть никакого "текущего значения итератора". А IEnumerable может быть не только поверх коллекции.


                                                                                                                В Вашем фрагменте кода попробуйте передать итератор как параметр — я тоже попробую) и сделать Current

                                                                                                                Но зачем мне передавать итератор как параметр, если все стандартные API работают с IEnumerable? Зачем мне создавать себе дополнительные сложности? Потому и в условии изначальной задачи был IEnumerable.

                                                                                                                  +3

                                                                                                                  bobby001, извините, вы точно уверены, что понимаете как работают итераторы в C#? С каждым вашим постом у меня всё больше складывается впечатление, что вы не до конца понимаете о чём говорите...

                                                                                                                  • UFO just landed and posted this here
                                                                                                                    0

                                                                                                                    Тем временем вот вам еще один пример итератора, который не поддерживает Reset. А его вы получите, скажем, из File.ReadLines, системного вызова. И еще один, после которого становится понятно, что от Reset скорее надо ожидать эксепшн, чем что-либо еще.

                                                                                                                    • UFO just landed and posted this here
                                                                        • UFO just landed and posted this here
                                                                            0

                                                                            Вот только для Join это принципиально невозможно. Вы точно понимаете, что делает Join?

                                                                            • UFO just landed and posted this here
                                                                                +1

                                                                                Ээээ… вообще не понимаю, что вы имеете в виду.


                                                                                Вот есть коллекция заказов, в каждом заказе есть идентификатор покупателя, есть рядом коллекция покупателей (с идентификаторами). Что, по вашему, делает операция join по идентификатору покупателя?

                                                                                • UFO just landed and posted this here
                                                                                    0
                                                                                    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


                                                                                    И как же они "объединяются"?

                                                                                    • UFO just landed and posted this here
                                                                                        0

                                                                                        Я не знаю, что такое "одномерная табличка". Можете просто результирующие строчки написать?

                                                                                        • UFO just landed and posted this here
                                                                                            +1

                                                                                            Но нет же. То есть вообще нет. Вы сделали группировку и сумму. А я просил join.

                                                                                            • UFO just landed and posted this here
                                                                                                +1

                                                                                                Ровно тем, что я написал выше: вы сделали группировку и сумму, которых я не просил.

                                                                                                  +3

                                                                                                  Ну и зачем вы SUM и GROUP BY сюда приплели?
                                                                                                  Напишите просто JOIN и покажите результат его работы.

                                                      0
                                                      «Выражение with по-прежнему будет копировать весь объект с сохранением типа среды выполнения:»

                                                      Если свойство не простой тип, а ссылочный, ссылка с обоих объектов будет на один адрес памяти?
                                                      –8
                                                      С# — молодец, не сдается! Его теснят и теснят другие языки, а старичок уходить не хочет!
                                                        +5
                                                        .NET Core одна из самых прогрессивных платформ текущего времени, о чём вы? Наверное думаете до сих пор, что это язык для десктопных приложений под винду? )

                                                        C# обошёл Java в удобстве, вдохновил появление Kotlin, является основным языком для самой популярной среды разработки игр Unity, поддерживается Xamarin'ом, да и WebAssymbly скорее всего массово придёт в наши браузеры через Blazor, потому что всё остальное отстаёт сильнее.
                                                          0
                                                          Не спорю, камрад. Просто так нечасто бывает, что язык/технология появилась в одной среде, заслужила там со временем почет и уважение, а потом по мере устаревания смогла приспособиться к совершенно другой среде, и там очень неплохо себя показывать. Поэтому C# — респект.
                                                            0

                                                            Про какую среду вы говорите? Web vs Desktop?
                                                            Так Asp.Net Web Forms появился с самого начала и на нем сколько всякого энтерпрайза написано. Переписывать и переписывать. Так что C# в вебе всегда был и будет))

                                                        +3
                                                        Больше интересовали апдейты для .net 5. Делаем перформанс тесты и не верим глазам)). На ровном месте 5-10 процентов прироста. В некоторых случаях и того больше. Оч круто.

                                                        devblogs.microsoft.com/dotnet/performance-improvements-in-net-5

                                                        Вот статья, кто еще не видел.
                                                          0

                                                          Даже завидно как-то по белому.

                                                            0

                                                            Я один тут вижу влияние фанатов Typescript? (P.S я сам веб разработчик на ts и то, куда он сейчас катится, мне не очень нравится) Не так давно смотрел описание RC для Typescript 4.1, там народ тоже с ума сошел в плане добавления новых видов дженериков… Ах, да… Это же одни и те же люди…


                                                            Мне лично кажется, что подобными конструкциями для расширения типов будут созданы только проблемы… Из полезного вижу только init'ы и switch шаблоны

                                                              0
                                                              Именно. Токсины Typescript-щиков или кого-то ещё подъедают устоявшиеся конструкции. И всё это, как мне кажется, не от желания улучшить, а от непонимания или неумения мыслить в ключе ООП. В общем, красивый строгий язык обретает довольно вульгарные отростки.
                                                                +1

                                                                Мне вот кажется, что это не от вульгарности отростков, а вот вашего непонимания задач этого кода.


                                                                А кто, собственно, сказал, что нынешняя команда C# должна мыслить в ключе ООП?

                                                                  0
                                                                  C# это объектно-ориентированный язык. Нужно что-то другое, пожалуйста, букв много: E#, K#, L#, M#, N#…
                                                                    +3
                                                                    C# это объектно-ориентированный язык.

                                                                    Это уже очень давно не чисто объекто-ориентированный язык. Википедия, прямо скажем, честно пишет: "multi-paradigm programming language encompassing static [...] functional, [...] object-oriented (class-based), [...] programming disciplines".


                                                                    И это, что характерно, к лучшему.

                                                                  +3
                                                                  «Вульгарные отростки» — это всё лишь очередные и очевидные шаги в сторону удобства использования C# в рамках функциональной парадигмы (records, деконструкторы, улучшения в patterns matching и т.д.). И это хорошо, потому что C# богатый мультипарадигменный язык, где функциональная часть немного (или много?) хромает, а уж с ООП частью всё в порядке добрые десятки лет.
                                                                  И опыт показывает, что понимание и умение мыслить в ключе ООП явно не достаточно, чтобы getting things done без боли и страданий. Иначе мы бы все сейчас пользовались условной Java 5 или C# 2.0.
                                                                +1
                                                                На мой взгляд, самое жуткое, что случилось с шарпом — «программы верхнего уровня». Если такое станет тенденцией — прощай, понятность благодаря структурированности. Можно прикрутить функциональщину, можно много чего прикрутить, но откручивать нечто (чёткую структуру) от системы, делать фактически исключение, которое нужно теперь запоминать и знать, и говорить, что так понятнее — это странно. Тем более, что открываешь IDE, делаешь проект — и вот оно всё, прописано и понятно.
                                                                  +1
                                                                  Ну это мелочи, право слово. Вообще не вижу ситуации, где это может как-то смутить, разве что совсем новичков. Просто убрали немного бойлерплейта в интуитивно понятном контексте.
                                                                    0
                                                                    Именно это, конечно, мелочь. Но звоночек пугающий. Выпилили пустую переменную, выпилили структуру типа для простоты, потом сделают using, как namespace в PHP, потом ещё какого сахара (или вообще — тьфу-тьфу — неявного «ясного» поведения) добавят — и аккуратный шарп превратится не то в JS, не то в Python.
                                                                      0
                                                                      Я примерно догадываюсь, от куда ноги у этого страха растут (всё завалят фичами и сахаром, настанет хаос и апокалипсис). В своё время обожглись на С++локе, но потом передули на Javоду, извините за каламбур.
                                                                    +3
                                                                    Они распространяются на один единственный метод (Main) на всю программу. И добавили их фактически чтобы всякие однофайловые лямбды писать было проще без лишних отступов, ну и для обучения, по вкусу. Какие тенденции, какие потери структурированности, вообще не понятно.
                                                                    0

                                                                    Можно теперь написать
                                                                    foreach (int i in 0..n);
                                                                    если энумератор к range прикрутить.

                                                                      0
                                                                      так можно или нельзя
                                                                  • UFO just landed and posted this here
                                                                      0

                                                                      Потому что будет сумасшедший breaking change. Одного этого аргумента достаточно уже.

                                                                    • UFO just landed and posted this here
                                                                        0
                                                                        Может не быть совместимости, наверное в этом проблема

                                                                        Не "может не быть", а "точно не будет". Это ровно то, что я написал выше: сумасшедший breaking change.

                                                                        • UFO just landed and posted this here
                                                                            0
                                                                            Мож ещё придумают…

                                                                            Что придумают? Как сделать это не-breaking change? Вероятность этого пренебрежимо мала, разве что это будет параллельная существующей иерархия типов (то есть, фактически, две системы типов вместо одной, а кому это надо?).


                                                                            придумали же, например null-значения в ненулевых типах, например bool?

                                                                            Нет, в типе bool как не было, так и нет null. null есть в Nullable<bool>, а это совсем другой тип.


                                                                            Правда, в GetString одни иероглифы…

                                                                            Не знаю, никогда не видел иероглифов в GetString (если, конечно, я изначально не положил эти иероглифы в данные). Может быть, дело в том, что у меня "стандартная" англоязычная локаль? Но с nullable-типами это точно никак не связано.

                                                                              0
                                                                              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

                                                                              Никаких иероглифов.

                                                                              • UFO just landed and posted this here
                                                                                  0

                                                                                  В Type.ToString я тоже иероглифов никогда не видел. Но, впрочем, дело даже не в этом. Дело в том, что поведение typeof(bool).ToString() не менялось когда добавили Nullable<T>. И это — обратная совместимость, так что на какие "побочные эффекты" вы жалуетесь, не очень понятно.

                                                                        Only users with full accounts can post comments. Log in, please.