Комментарии 69
Теперь в C# 7 добавилась поддержка сеттеров, геттеров, конструкторов и деструкторов:
Вы имели ввиду финализатор?
А финализатором тогда можно назвать именно метод
protected override void Finalize()
Некоторая путаница с переводами понятий.
И ведь под капотом оно всё равно создаёт специфичный для каждого каждого использованного дженерик-параметра код, но синтаксиса для того, чтобы явно определять поведение для некоторых типов так и нет.
Приятный? Паттерн матчинг выглядит, мягко скажем, уродливо.
Сравните с f#
type Color =
| Red = 0
| Green = 1
| Blue = 2
let printColorName (color:Color) =
match color with
| Color.Red -> printfn "Red"
| Color.Green -> printfn "Green"
| Color.Blue -> printfn "Blue"
| _ -> ()
Да и поддерживается он очень ограничено. Опять же, сравните с F#
Был и мне кажется что switch это самый не удобный оператор притянутый их с++ лучше бы его упростили то примерно такого
switch(shape)
{
Circle c:
WriteLine($"круг с радиусом {c.Radius}");
Rectangle s when (s.Length == s.Height):
WriteLine($"{s.Length} x {s.Height} квадрат");
Rectangle r:
WriteLine($"{r.Length} x {r.Height} прямоугольник");
null:
throw new ArgumentNullException(nameof(shape));
default:
WriteLine("<неизвестная фигура>");
}
Ключевое слово «case» должно быть. А вот «break» напрягает.
… и сложно парсерное нечтопарсер кода уже написан — Roslyn, бери и пользуйся. Это не проблема.
Что плохо, так это возможность обфускации кода, например, объявление переменных в виде:
if (0 is var start && 100 is var count) ...
Можно, конечно, вместо
catch (...) when (condition)
{
...
}
написатьcatch (...)
{
if (condition)
{
...
}
else throw;
}
, но семантика тут будет другая. В первом случае не будет осуществляться разворот стека.catch when это exception filters, фича низкого уровня ранее недоступная C#.(она есть в VB.NET)
using это сахар, он разворачивается в try finally Dispose.
Шаблоны наиболее полезны для работы с кортежами и подобными им объектами (как case class в scala). Странно, если это не реализовано.
Объясните мне кто понял про кортежи System.ValueTuple.
С одной стороны там Item1, Item2, также как в обычном System.Tuple, с другой стороны можно дать осмысленное имя полям — Id, Name.
Вот если я напишу публичную функцию
public (long Id, string Name) GetDto(){ return (1, "1");}
А потом её зареференсю из другой сборки, то что я увижу — Item1, Item2 или же Id, Name ?
Моё мнение, что я должен увидеть Item1, Item2, потому что System.ValueTuple это struct и он не изменяем — но как тогда сохраняется семантика, что первый элемент кортежа назван Id, а второй — Name?
Кто нибудь разбирался, как это под капотом работает?
System.ValueTuple
.То есть ваш код будет скомпилирован в что-то вроде этого:
[return: TupleElementNames(new[] { "Id", "Name" })]
public ValueTuple<long, string> GetDto(){ return (1, "1"); }
Почему я могу написать метод-расширение:
public static IData<T> Add<T1, T2>(this IData<T> lhs, IData<T> rhs)
но не могу:public static IData<T> operator + (IData<T> lhs, IData<T> rhs)
?Однако операторы перегружаются и вы можете сломать написанный код который использует оператор.
Если же вы разрешите определять операторы только там где их нет, то расширения сломаются когда оператор определят в самом типе.
как по мне, так C# берет часть фишек из функциональных языков, и зачастую это делает код более читаемым и менее многословным. Но везде нужно знать меру, конечно
Недавно обсуждали в комментах, что C# давно уже движется в сторону забытого нынче Nemerle… высока вероятность, что где-нибудь через 3-4 версии у них будет паритет по фичам и примерно одинаковый синтаксис.
Но вот использовать его как основной язык в проекте проблематично, по той причине, что нету для него внятных решений типа ReSharper.
Возможно, когда команда Jetbrains допилят проект Nitra и на нем уже построят следующую версию Nemerle, всем будет счастье.
По моему это просто чудесно!!! :) Наконец это появилось и в C#!
1)Область применения
Лямбда применяется, когда на вход требуется делегат типа Func, Action, Predicate, обработчик события и прочее. И нужно это, дабы не плодить миллионы однострочных хендлеров для миллионов входных типов, улучшить читабельность и концентрацию, чтобы не носится как бешеный по всему исходнику. Плюс позволяет не создавать отдельный метод, если он нужен только один раз.
Локальная функция имеет те же преимущества, но позволяет обернуть любой код. Например, если нужно посчитать несколько значений по одному и тому же алгоритму.
2)Могу ошибаться, но логично предположить, что реализация у них разная.
Лямбда аллоцируется в памяти, а локалка, скорее всего, работает по принципу inline-функций и разворачивается на этапе компиляции.
Как по мне локальные функции будет очень удобно использовать для реализации какой-либо рекурсии (собственно что и продемонстрировано в примере). Часто есть какая-то оборачивающая public функция, которая внутри себя инициализирует какую-то структуру и передает ее уже основной внутренней рекурсивной функции. Эту структуру можно передавать с помощью аргументов, а можно с помощью полей класса. Оба эти решения не очень хорошо выглядят: в первом случае придется каждый раз передавать эту структуру через рекурсивные вызовы (избыточность и избыточное потребление памяти стека), а в другом — добавляется побочный эффект. Локальные функции как раз и решают обе этих проблемы: аргументы не нужно передавать через сигнатуры, но с другой стороны все объекты являются локальными.
У лямбд более громоздкий синтаксис. Требуется явно указывать тип делегата, из-за чего нельзя воспользоваться анонимным типом. Кроме того, если лямбда рекурсивна, требуется двойная инициализация переменной.
Пример из статьи на C# 6 выглядит так:
public int Fibonacci(int x)
{
if (x < 0) throw new ArgumentException("Не надо негатива!", nameof(x));
Func<int, Tuple<int, int>> Fib = null;
Fib = i =>
{
if (i == 0) return Tuple.Create(1, 0);
var p = Fib(i - 1);
return Tuple.Create(p.Item1 + p.Item2, p.Item1);
};
return Fib(x).Item1;
}
Что нового появилось в C# 7 и уже поддерживается в Visual Studio “15” Preview 4