Как стать автором
Обновить

Комментарии 133

Замечательно. Хороший и доступный обзор. Спасибо.
А ничего что ни в одной из ваших ссылок нет именно обзора добавлений к синтаксису? Нет бы спасибо автору сказать
*Нет бы спасибо автору перевода сказать. Так или иначе, обзора новшеств синтаксиса, мне кажется, на хабре до сих пор не было
А есть ли информация как открытие ядра и pull-request'ов сказались на изменениях в производительности? Там было много хороших изменений даже за столь короткий срок.
Еще не было релиза после открытия ядра, чтобы предоставлять статистику. Да и времени не очень много прошло. Но возможно через какое-то время МС будет подводить итоги и наверное про перформанс тоже что-то напишут. А пока только в отдельных чейнжах можно найти локальные показатели, например:
Improve hashcode usage in ConcurrentDictionary
Fast implementation of Hamming weight
Я очень сильно сомневаюсь, что оптимизация Хемминга в реальном приложении даст хоть сколько-нибудь заметные результаты. Целевая функция дёргается один-единственный раз где-то в рефлексии. В коммите скорее отражено стремление делать даже мелкие вещи хорошо =)
Я вообще сильно сомневаюсь, что в рамках всего фреймворка открытие платформы даст какие-то значительные результаты :). Только в каких-то отдельных узких кусках/сценариях, которые «проглядели».
Ну я скажу что огромное количество мусора из Xml'а там выгребают до сих пор, как минимум это огрмный плюс =)
ну, прям скалу догоняют семимильными шагами :)
и с чего минусы? есть опыт и то и другого. кое-что из того, что описано, доступно в скале.
команда c# не придумало все это с нуля.
солидарен
хотя не совсем правильно сравнивать C# и Scala

Правильно, Scala — это розовая мечта Джавы, которую C# уже воплощает в реальность :)
C# объективно далеко до Scala в плане синтаксического сахара, использовании функционального подхода и асинхронности
А можете дать ссылку на какой-нибудь обзор хороший?
а я таки читая обзор, подумал, что майкрософт себе нанял пару пхпшников
Мухаха, scala ненавистники лютуют
А так по сути это не скалу догоняют а из скриптовых языков сахар перетягивают
Это сильно уменьшает количество кода

Ключевое слово return пропало, а скобки заменились стрелочками. Сомнительный профит, зато человеку, который пришел из C\C++ будет куда труднее привыкнуть.
На конкатенацию строк бы посмотрел в IL, а nameof — выглядит неплохо.

В общем и целом C# радует направлением своего развития.
конкатенацию строк бы посмотрел в IL

Это просто сахар для String.Format
Да, Вы правы — это сахар, добавил IL код в статью для наглядности.
Класс, спасибо! Вот это я понимаю отзывчивость автора статьи)
Интересно почему не сделали сразу на месте конкатенацию, а вместо этого оставляют все до рантайма.
Вы про nameof или интерполяцию? Если про второе то это как минимум странный вопрос. Как вы интерполируете строку в compile time?
Я про интерполяцию.
Почему бы не применить string.Concat, сложив отдельные части в одну строку?
Потому что в интерполяции можно указывать форматспецифаер, как минимум…
Я понимаю, что проще всего было просто свести весь код к string.Format, но чисто технически вполне возможно применить и форматспецифаер для каждого интерполируемого элемента перед склеиванием.
string.Format в любом случае будет парсить строку-форматтер и на ее основе производить какие-то действия. Все эти действия могут быть закодированы на этапе компиляции.
Да о чем речь, string.Format даже не кэширует уже разобранные строки, хотя кэш из хотя бы 100 последних использованных уникальных строк было бы достаточно, чтобы хорошо поднять производительность метода… Так что реализуется самый простой способ, а если кому-то нужен производительный — пусть пишет для себя сам (с) Официальная позиция Эрика Липперта, когда его спросили, почему в LINQ не хватает некоторых очень важных методов.
String.intern?
Это не только сахар для String.Format, там в зависимости от ситуации разный код может генерироваться, даже отдельный тип вводится. Ну и при JIT компиляции это тоже может оптимизироваться во что угодно, тут никаких гарантий.
НЛО прилетело и опубликовало эту надпись здесь
а nameof — выглядит неплохо

Выглядит как попытка исправить один костыль другим костылём. Интерфейс INotifyPropertyChanged вообще одно из самых сомнительных решений в стандартной библиотеке
Ну почему же сразу этот интерфейс? nameof очень нужен для упрощения построения Expression — не всегда же такое выражение можно задать лямбдой.
NameOf работает на всех членах, типах и т.п., он не ограничен свойствами. И оператор много где можно применить, хоть INPC и самый популярный:

1. Argument exceptions
2. MVC action links
3. XAML dependency properties
4. Logging
5. Attributes (e.g. debugger display)
Как строка, iirc.
Не понял, результат будет выглядеть как строковый литерал или как новый опкод?
Первое.
Вот пример как это выглядит в IL
Код в C#:
var value = nameof(System.Console);

Код в IL
IL_0000: nop
IL_0001: ldstr "Console"
IL_0006: stloc.0
IL_0007: ret

Просто на этапе компиляции узнается имя класса, метода и т. д.
Сомнительный профит, зато человеку, который пришел из C\C++ будет куда труднее привыкнуть.

Приятная мелочь в итоге, это я говорю после 5 лет разработки :)

Ох и старый же топик! Да, сейчас кажется вполне себе привычной штукой, глаз давно привык. Зато вот новые ре'корды… :)

Только второй пункт не конкатенация, а интерполяция строк.
Спасибо, исправил.
Это не with, потому как область видимости всегда — файл целиком, и импортировать так можно только члены статических классов (которые, по сути, являются модулями).

>область видимости всегда — файл целиком
Тогда ещё вреднее, чем with. Читать такой код непросто будет.
Вы так толком и не объяснили, почему именно. До C# 6 using работал точно так же, но был ограничен пространствами имен (т.е. можно было импортировать имена типов из неймспейса, но не имена методов из класса). Теперь он работает и с классами тоже. В чем принципиальная разница?

Или вы, когда пишете код на C#, всегда пишете System.Collections.Generic.List<System.String>?
Все просто using научился добавлять статические классы, вот пример по нагляднее:
using System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Hello Habr!");
        }
    }
}
Это понятно. Вопрос (к автору оригинального комментария) был о другом — что плохого в расширении функционала using до импорта членов классов? Чем классы хуже неймспейсов в этом смысле, с его т.з.
Скажу в помощь автору: я вижу только одно неудобство — когда скачал чужой пример кода, а он не скомпилировался из-за пропущенных References и ты начинаешь ломать голову где что объявлено. Сейчас помогает знание неймспейса, а так придётся их перебирать. Случай редкий, но бывает.
Думается мне, что решарпер научится (если уже не научился) находить подходящие классы для импорта. Например, если в солюшене хоть один проект референсит сборку с классом, имеющим подходящий метод, то решарпер должен его предложить.
Иногда не мог решарпер. Ну вот было такое пару раз.
ЕМНИП, редактор в Roslyn уже умеет находить отсутствующие референсы. Думаю, его научат делать это и в таких случаях.
Я не автор комментария, но все дело в ухудшении читаемости кода.
Предположим, что Вы видите только кусок:
        static void Main(string[] args)
        {
            WriteLine("Hello Habr!");
        }

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

Было бы интересно почитать про реальную мотивацию для включения подобного в C# 6.0.
Ну, если быть до конца последовательным, то нельзя сказать что-то наверняка даже имея кусок:
Console.WriteLine("Hello Habr!");

Только так:
global::System.Console.WriteLine("Hello Habr!");
В C++ имеется практика писать именно так, полностью (но там цепочки короче, да и проблемы могут возникнуть от using namespace).

Ну и, все-таки, при написании
Console.WriteLine("Hello Habr!");
WriteLine с гораздо меньшей вероятностью окажется именно методом текущего класса.
Методом текущего — да. Но он может оказаться методом объекта, доступного из текущего через свойство Console.

В общем случае, вам все равно придется посмотреть, откуда оно. Что, впрочем, в любой нормальной IDE делается одним нажатием (или одним движением мышой).

А в конкретном случае, если вы видите WriteLine или там Abs, я не думаю, что у вас реально возникнет такой вопрос.
Так дело в том, что раньше мы были избавлены от подобного для нестатических методов. А теперь и для них тоже придется смотреть.

А в конкретном случае, если вы видите WriteLine или там Abs, я не думаю, что у вас реально возникнет такой вопрос.
Зато, боюсь, в соглашениях о кодировании скоро может появиться новый пункт «не использовать using static для вызова небиблиотечных статических методов».
> Так дело в том, что раньше мы были избавлены от подобного для нестатических методов. А теперь и для них тоже придется смотреть.

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

Я не вижу особой проблемы с тем, чтобы посмотреть. Если вы видите что-то непонятное, вроде Foo(), то вам в любом случае придется смотреть, что оно делает, и неважно, в вашем оно классе, или нет (и сама по себе информация о том, это instance или static метод, ничего важного не несет).
Я имею в виду, что если я сейчас вижу в коде WriteLine(), то я уже понимаю, что это метод того класса, с которым я сейчас работаю. Но в 6.0 это может быть и метод другого класса.

Я вижу такой метод и читаю код дальше. Потом работаю с этим кодом, дополняю его различным функционалом. Потом, например, пишу тесты на этот код. И половина тестов у меня валится. Так как на самом деле в этом коде есть неочевидная зависимость, работающая с неким статическим состоянием, которую я сразу не смог обнаружить. Приходится делать лишнюю работу либо после (исправляя все и делая рефакторинг), либо до (проверяя каждый метод, чем именно он является).
А если в коде есть очевидная зависимость, работающая с неким статическим состоянием, которую вы сразу обнаружили — вам не придется делать рефакторинг?
В этом случае мне придется делать только рефакторинг чужого кода. А свой я еще не успею написать, так как заранее увижу проблему.
Было бы интересно почитать про реальную мотивацию для включения подобного в C# 6.0.

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

Что вы имеете в виду под «локальными функциями», несколько непонятно. Вложенных методов в C# нет, так что максимум, что вы можете сделать, это написать метод-обертку на вашем классе. Но зачем, если using System.Math сделает то же самое в несколько раз короче? Ваш код от этого понятнее не станет.
Я не про перегрузку операторов, а про «символьные» операторы, вроде тех, что сделали в F#: habrahabr.ru/sandbox/35494/

Под «локальными функциями» подразумеваются лямбды, обьявленные в области видимости метода:

class c
{
     public static int Calculate (int abstractValue)
     {
           System.Func<int,int> abs = v => Math.Abs(v);
           System.Func<int,int,int>max= (v1, v2) => Math.Max(v1,v2);
           return max(abs(abstractValue), abs(abstractValue - 100));
     }
}
Код вырвиглазный, если честно. Оно, к слову, нормально оптимизируется?
Оно, к слову, нормально оптимизируется?

Скорее нет, чем да. Я бы не использовал такую конструкцию там, где остро стоит вопрос производительности. Но если у вас одно и то-же действие повторяется кучу раз, и в той-же степени оно больше нигде не требуется, то код, таким образом, выносится в локальную функцию. Лишних методов нет, а код самого метода стал чуть менее избыточным.
А, понятно. Но с лямбдами, во-первых, вы платите за оверхед, во-первых, создания делегата, а во-вторых, виртуального вызова через него (насколько мне известно, JIT не умеет это оптимизировать). Ну и вам придется расписывать полностью все типы в сигнатуре. Т.е. по сравнению с приватным методом, вы выигрываете только более ограниченную область видимости, но за счет производительности. Если вам нужен Math больше, чем пару раз, то, скорее всего, производительность важна :)

Идеальным было бы, если бы using можно было писать внутри классов и методов…
Идеальным было бы, если бы using можно было писать внутри классов и методов…

Вероятно, так и хотели, но, вероятно, побоялись обвинений в дельфиомании.

Ну и вам придется расписывать полностью все типы в сигнатуре.

В сигнатуре самой лямбды вроде-как не обязательно, нет? Из-за того, что все типы указаны в типе локальной переменной, куда эта лямбда пихается.

вы платите за оверхед

В меру можно всё. Даже лямбды вместо методов и сложение строковых литералов.
Вероятно, так и хотели, но, вероятно, побоялись обвинений в дельфиомании.

Скорее решили не усложнять. Всё-таки «using namespace» внутри функций в плюсах есть, и им следовать — ещё страшнее, чем следовать дельфи. :)

Даже лямбды вместо методов и сложение строковых литералов.

Конкатенация оптимизируется, емнип.
Скорее решили не усложнять. Всё-таки «using namespace» внутри функций в плюсах есть, и им следовать — ещё страшнее, чем следовать дельфи. :)


Тем не менее, фича крайне спорная. И моё не-паучье чутьё мне подсказывает, что рано или поздно у меня появится желание загнать булавку под ноготь тому, кто одним using'ом превратит код в кашу.
рано или поздно у меня появится желание загнать булавку под ноготь тому, кто одним using'ом превратит код в кашу

Существующих фич уже предостаточно для написания говнокода. Плюс-минус одна фича — не решает. :)

Плюс дизайнеры языка всё-таки исключили самые спорные части — свойства, extension методы и т.п. нельзя импортировать через using static. Найти классы с большим количеством статических методов — не так уж и просто. Хотя… using static Tuple и т.п. — и вперёд с непонятными Create… Наверное, поводы для опасений всё же есть.
>> Всё-таки «using namespace» внутри функций в плюсах есть, и им следовать — ещё страшнее, чем следовать дельфи. :)

На самом деле вполне нормально, если не писать функции на два экрана :)
System.Func<int, int>      abs = v        => Math.Abs(v);
System.Func<int, int, int> max = (v1, v2) => Math.Max(v1, v2);

Func<int, int>      abs = Math.Abs;
Func<int, int, int> max = Math.Max;
Вы предлагаете в каждый класс с вычислениями пихать зоопарк функций из Math? Нет, спасибо. :)
Ну, если можно себе позволить маленькую шалость, то почему нет?
Да в финальной версии будет using static, сейчас просто using.
О, здорово, будущее уже наступило — сейчас тестирую возможности согласно списку, был удивлен, что на «using NameSpace.Class» ругается и предлагает заменить на using static. Благо в комментариях уже был ответ :)
Ну это никто и не заставляет использовать, так как по сути это побочный эффект от импорта статических классов в результате которого становятся видны экстеншен методы.
Учитывая, как лениво написана статья, есть ненулевая вероятность, что это брехня, и extension методы таким образом не импортируются. Свойства тоже не импортируются, вроде. Соответственно, это сокращение кода при использовании классов с кучей статических методов типа Math, где имя класса — это выверт труЪ-оопности, а не что-то реально нужное и полезное.
>> В C# 6.0 добавлена возможность инициализации Dictionary по ключу значения.

Возможность такая была давно — new Dictionary { { «key», «value» },… }. Добавили новый синтаксис, который мапится на индексер вместо Add.
Писали об этом уже много раз. И вы про кучу фич забыли. Где read-only свойства? Где конструкторы структур без параметров? Я уж молчу про отсутствие эволюции фич, обоснований и вообще какой-либо дополнительной информации.
Read-only свойства?
Вы уверены что их небыло и раньше?

class c
{
    public static object Property { get; }
}

Имеются в виду read-only автоматические свойства. Вот тот код, что вы написали, например — попробуйте его скомпилить в C# 5.0.

Там нужны были инициализаторы, а их не было.
Из вашего комментария было не ясно, подразумеваются ли read-only как таковые или инициализация автоматических read-only свойств.
Кстати, у меня есть вопрос по поводу конструкторов структур без парамеров — когда он будет срабатывать? При явном вызове new или каждый раз при неявном создании нового экземпляра структуры?
При вызове new. Если создавать массив, то он будет забит нулями, как и раньше.
>>value type
>>массив
>>нулями
Не понял
Т.е. все поля имеют значение по умолчанию — нули для числовых типов, false для bool etc.
var t = new T() — будет вызван конструктор без параметров.
var t = new T[N] — остаётся как раньше, конструкторы не вызываются.
Вопросиков натыкал и null reference exception идет лесом!

string departmentName = emp.Department.Name;
string departmentName = emp?.Department?.Name;
Swift-style
А с departmentName вы что будете делать? :)
double powerValue = Math.Pow(2, 3);
double roundedValue = Math.Round(10.6);

Вспомнился старый добрый Delphi.
Синтаксический сахар. То что они сделали с null проверками — приятно, не надо будет лишних конструкций городить.

Насчет интерполяции… очень спорно: Regex, SQL да и всякие такие строковые команды всегда было сложно дебажить, особенно когда они большие. А тут, большие строки, станут еще больше и будут встречаться не подсвеченные «винегреты», которые, не дай бог, написаны в одну строку. Останется подсолить их многоуровневыми if, засунуть их в какой-нибудь SQL запрос и это читать будут только под дулом пистолета.
На то человек и считается существом разумным, чтобы сахар не использовать бездумно, а там, где от него будет польза и больше читаемости ;)
много сахара вредно, это ещё диетологи говорят!
Я искренне надеюсь на то, что строки будут подсвечены правильно. Иначе действительно атас
Должна быть подсветка
image
Поработаю капитаном, VS2015 Update1 R#10.2
image
Подсветка там будет, не беспокойтесь :)

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

Т.е. фича несколько спорная, но хуже она точно не сделает…
Думается мне, что выражения в строках будут и подсвечиваться, и проверяться на стадии компиляции. Не надо преувеличивать масштаб катастрофы, это всего лишь мелкое удобство для нелокализуемых мелких программок. За использование интерполяции для генерации SQL расстреливать надо, как сейчас расстреливают за любую сборку SQL строками.
static class A
{
    public static int sum(int a, int b) => a + b;
}
    
static class B
{
    public static int mult(int a, int b) => a * b;
}

using static System.Console;
using static A;
using static B;

class Program
{
    static void Main(string[] args)
    {
        var s = sum(10, 20);
        var m = mult(s, 2);
        WriteLine(m);
    }
}

Здравствуй, процедурное программирование?
От того, что Вы напишете имена классов перед именами методов, менее процедурным оно не станет.
Null-условный оператор
Может быть Safe navigation operator?

catch (Exception ex) if (ex.InnerException != null)
ИМХО, по аналогии с generic, здесь бы больше подошло where вместо if (т.к. второе ассоциируется с неким последующим действием, а первое — именно фильтрация по условию).
Фильтрация тут, вообще, экономия на паре скобок.
Да, действительно, минус rethrow.
Там есть еще отличия. Из очевидного — в IDE, если у вас включен break on exceptions, то с паттерном catch-throw это будет расцениваться, как новый эксепшн, со всеми вытекающими. С фильтром — нет.

Из неочевидного — фильтры выполняются на первом проходе по стеку, когда определяется, в какой catch-блок перейти — и до отмотки стека. Т.е. всякие там finally и using от точки выброса до фильтра еще не отработали. Иногда это бывает полезно (а иногда — это очень хорошо запрятанные грабли).
Повторное выкидывание исключения стирает старый стектрейс и обманывает отладчик. Фильтр же не обладает таким недостатком.
Я сообразил уже, спасибо.
Разве?
throw и throw чем отличаются?
А еще можно в innerException поместить исходное исключение со всем стек-трейсом.
throw и throw чем отличаются?
Ничем. Но вы, наверное, хотели сказать что-то другое?)
А еще можно в innerException поместить исходное исключение со всем стек-трейсом.
Не всегда такой вариант лучший.
throw и throw чем отличаются?
Ничем. Но вы, наверное, хотели сказать что-то другое?)

Да, другое)
throw и throw ex чем отличаются?
где ex — перехваченное исключение.
Ничем. Это разные записи одного и того же — соответственно, они одинаково портят стектрейс и одинаково обманывают отладчик.
Разве простой throw стирает stack trace?
Да.
Очень странно. Здесь пишут:
A throw statement can be used in a catch block to re-throw the exception that the catch block caught. In this case, the throw statement does not take an exception operand.

Если не ошибаюсь, у Рихтера было написано другое, что исключение перегенерируется так, как будто не перехватывалось.
Или, как и в некоторых других моментах, он писал о конкретной реализации, чего спецификация не гарантирует?

В любом случае, появление фильтров исключений радует, т.к. в этом случае фильтрация будет происходить средствами CLR, в то время как если мы фильтруем исключение в catch кодом, то в общем случае этот код может и не сработать, если исключение «фатальное».
Это разные записи одного и того же — соответственно, они одинаково портят стектрейс и одинаково обманывают отладчик.

Неправда. С предупреждением.
Извиняюсь, был не прав. throw без параметров действительно не портит стектрейса.

Однако, тем не менее, обе записи одинаково обманывают отладчик.
Я предполагал, что именно поведение отладчика сбило вас с толку.
Почему так с отладчиком происходит, понятно.
Именно поэтому поддержку в C# фильтров исключений можно только приветствовать.
Null-conditional operators — его называют, а так то его можно назвать как угодно.
Скорее Null-propagating operator
т.к. второе ассоциируется с неким последующим действием, а первое — именно фильтрация по условию

что асоциируется с guard'ами а не с последующим действием
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории