Pull to refresh

Comments 36

Ну назвать основную фишку Generic Math не интересной - это такое

Я не называл Generic Math не интересной фишкой :). Если вы про то, что в статье не рассказывается про неё, то хочу заметить, что я дал ссылку на подробную статью по разбору нововведений C# 11.

Имхо это лучше обобщить через статические методы в интерфейсах + утиную типизацию. Статические методы в интерфейсы, наконец-то, завезли, а вот утиную типизацию в широком смысле ещё нет.

Тогда это распадается на синтаксический сахар конкретного языка.

Native AOT шикарная тема если сравнивать с Go. У Go было преимущество маленький размер приложений, что удобно в утилитах. В шарпе приходилось качать весь рантайм, в т.ч. в контейнер. Теперь в C# приложения такие же маленькие как и в Go, и контейнеры станут много легче. Go можно выкидывать.

С Go не знаком, но не могу не согласиться :). Native AOT выглядит очень круто.

А вот я, например, уже после девятой версии самого C# и пятой версии .NET совсем перешёл на golang.

Всё конечно круто и хорошо, и все эти финтифлюшечки и дополнения, но вот сам язык стал просто ужасным. Его превратили в какое-то подобие javascript. Весь этот синтаксический сахар стал просто неперевариваемым. Одни только switch expressions вызывают тихий ужас.

static Point Transform(Point point) => point switch
{
    { X: 0, Y: 0 }                    => new Point(0, 0),
    { X: var x, Y: var y } when x < y => new Point(x + y, y),
    { X: var x, Y: var y } when x > y => new Point(x - y, y),
    { X: var x, Y: var y }            => new Point(2 * x, 2 * y),
};

Зачем? Почему мне нужен turing-full-switch-operator?

ХЗ. Слишком перегруженно и verbose.

Хотя я на дотнетах писал с 2002 года...

Продемонстрируйте простыню golang для этого же :-).

Ну так там-то хоть понять можно, что там делается. Мне уж проще прочитать пару страниц if-statements, вместо того, чтобы пытаться вспомнить, что делает where в switch.

func Transform(p Point) Point {
	switch {
	case p.X == 0 && p.Y == 0:
		return Point{0, 0}
	case p.X < p.Y:
		return Point{p.X + p.Y, p.Y}
	case p.X > p.Y:
		return Point{p.X - p.Y, p.Y}
	default:
		return Point{2 * p.X, 2 * p.Y}
	}
}

Не такая уж и простыня. Го начал осваивать недавно; после того, как преодолел первоначальное отторжение, начал понимать, что в его минимализме многое хорошо. За счёт большего в среднем количества строк по сравнению с Питоном или тем же C# он первоначально производит впечатление более многословного языка, но на самом деле он более вертикальный, чем горизонтальный, если можно так выразиться. Строк больше, но каждая строка при этом краткая и быстро читаемая.

Во-первых Ваш код не особо лучше. ту и последовательность if-ов сгодилась бы

Во-вторых, учтите, что switch-выражение чаще применяются там где нет прямого возрата результат через return.

В C# switch-выражение не ограничивает применение аналогичного паттерна (приведённого Вами, который и в вашем напсании , в общме-то, тоже остаётся доступным в C# в рамках классической инструкции switch case).

Может, на первый взгляд, выгляди непривычно (я бы даже сказал немного кривовато), но вполне работоспособно, не громоздко и при быстром привыкании к такому шаблону вполне понятно и легко читаемо

static Point Transform4(Point p) => 0 switch
	{
			_ when p.X==0 && p.Y==0	=> new Point(0, 0),
			_ when p.X < p.Y 	=> new Point(p.X + p.Y, p.Y),
			_ when p.X > p.Y 	=> new Point(p.X - p.Y, p.Y),
			_            		=> new Point(2 * p.Y, 2 * p.Y),
	};

или так (если больше нравится)

static Point Transform4(Point p) => p switch
	{
			var n when n.X==0 && n.Y==0	=> new Point(0, 0),
			var n when n.X < n.Y 	=> new Point(n.X + n.Y, n.Y),
			var n when n.X > n.Y 	=> new Point(n.X - n.Y, n.Y),
			var n             		=> new Point(2 * n.Y, 2 * n.Y),
	};

Ниже в комментариях я привёл ещё пару вариантов - более привычных для C# - но требующих перехода от структуры к кортежу (ввожу вспомогательные функции). Вариант выше этого не требует - это просто прямой пример такой же реализации как и код выше - но в рамках switch-выражения. Показывающий неявные возможности switch-выражений.

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

Зато, switch-выражение имеет куда больше возможностей проверки образца, чем switch case инструкция

В такой записи не вижу принципиальной разницы

в сравнении с чем?

С такой записью?

static Point Transform(Point p) =>
				p.X==0 && p.Y==0 	? 	new Point(0, 0)
				: p.X < p.Y 		?	new Point(p.X + p.Y, p.Y)
				: p.X > p.Y 		? 	new Point(p.X - p.Y, p.Y)
									:	new Point(2 * p.Y, 2 * p.Y);
Чуть другое форматирование
static Point Transform(Point p) => 
  				p.X==0 && p.Y==0 	?	new Point(0, 0) :
				p.X < p.Y 			?	new Point(p.X + p.Y, p.Y) :
				p.X > p.Y 			? 	new Point(p.X - p.Y, p.Y) :
										new Point(2 * p.Y, 2 * p.Y);

Или Вы switch case инструкцию? Или про приведённые ниже в комментариях варианты с кортежем?

На мой взгляд switch-выражение всё-таки красивее выглядит

Новое switch выражение очень крутое, много чего может - тут ноги растут их функциональных языков и оператора сравнения с образцом + идея того, что каждая операция - это выражение (внедрять функциональные фишки в императивные ЯП нынче очень модно). Привидённый же Вами пример хоть и синтаксически верный, но на практике это очень редкий случай - всё-таки чаще будут применяться куда более простые инструкции. А сложные придут только на замену монструозным if операторам - причём последние без боя долго сдаваться не будут - программисты будут вибирать то, что написать проще - а указанная Вами конструкция не проще последовательности if-ов

А указаный пример на switch-операторе на мой взгляд лаконичнее выглядет через деконструкцию на кортежи

static class PointExt
{
	static public void Deconstruct(this Point p, out int X, out int Y)
	{
		X = p.X;
		Y = p.Y;
	}
}

public class Program
{
	
	static Point Transform2(Point point) => point switch
	{
			(0,0)                    	=> new Point(0, 0),
			(var x, var y) when x < y 	=> new Point(x + y, y),
			var p when p.X > p.Y 		=> new Point(p.X - p.Y, p.Y),
			(var x, var y)            	=> new Point(2 * x, 2 * y),
	};
}

Или вариант 2

static class PointExt
{
	
	static public (int X, int Y) Decons(this Point p, out int X, out int Y)
	{
		X = p.X;
		Y = p.Y;
		return (p.X, p.Y);
	}
	
}

public class Program
{
	ыtatic Point Transform(Point point) => point.Decons(out var x, out var y) switch
	{
			(0,0)      		=> new Point(0, 0),
			_ when x < y 	=> new Point(x + y, y),
			_ when x > y 	=> new Point(x - y, y),
			_            	=> new Point(2 * x, 2 * y),
	};
}

если подумать - наверняка найдётся ещё какой-нибудь лаконичный вариант применения switch-выражения. В общем - кортежи и функции расширения рулят

Только не говорите, что я написал куда больше кода - так как вводил функции расширения! Просто switch-операторы выражения больше затачивались под одинарные типы и под кортежи, чем под развёртывание кишок структур и классов, но поддерживают и операции над ними - коли надо будет!

И для switch-выражений найдётся куча других примеров (других) ситуаций - где они будут куда более лаконичными!

Это пример с docs.microsoft.com

Я просто к тому же. Я вообще не понимаю, нафига надо было мешать строгий и лаконичный C# с Яваскриптом?

Не поймите меня неправильно. Но меня напрягает то, что мне надо раз в 4-5 лет переписывать программы чтобы они компилировались. Это страшно. Не бывает так, что "написал и забыл". Разве что вы хотите переписывать проект с нуля каждые пять лет.

А вот в реальности все эти примочки только раздражают. Когда ты не можешь вспомнить язык. Это после 20ти лет написания ПО появляется такая точка зрения.

Это пример с docs.microsoft.com

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

Я вообще не понимаю, нафига надо было мешать строгий и лаконичный C# с Яваскриптом?

Как уже написал - тут смешивание с функциональным подходом, а не с JavaScript идёт. Это сейчас очень модно - скрещивать императивную и функциональную парадигму. И обычно это идёт только на пользу. А первым на подобные конструкции был не то Scala не то Nemerle

раз в 4-5 лет переписывать программы чтобы они компилировались. 

Зачем - C# достаточно стабилен в плане ломающих фич. switch-выражения ничего не ломают. И Вы можете использовать классические switch case инструкции.

Язык C# развивается - это многих привлекает. Но как и любого развивающегося языка со временем он обрастает большим количеством синтаксического сахара и вынужден тянуть устаревшие конструкции. Поэтому со временем приходят новые языки и потихоньку начинают вытеснять старые! И это нормально. Вот, например, сейчас такой процесс идё с Java и Kotlin. В своё время такой процесс был между Java и C#. Так же такой процесс идёт между Objective-C и Swift. Когда то было между C и C++, а сейчас ожидается что будет между C++ и Rust (но не факт).

В будущем продолжится. Я делаю прогноз - что придут более декларативные абстрагированные ЯП с глубокой интеграцией с AI-компилятором и AI-IDE - которые вытеснят все актуальные ЯП прикладного уровня

Что у вас скомпилироваться не может? На работе программы с .net 3.5 до сих пор крутятся и WCF используют и ничего переписать не нужно.
Вообще странно что камень преткновения для вас это switch, по моему опыту не самая часто используемая конструкция, а если где-то и используется то там чаще всего надо провести декомпозицию

Вообще странно что камень преткновения для вас это switch, по моему опыту не самая часто используемая конструкция, а если где-то и используется то там чаще всего надо провести декомпозицию

Мне кажется у Вас устаревшее мнение. Сейчас тенденция развития C# (да и многих других ЯП) в том, чтобы наоборот сделать switch основной конструкцией ветвления - по крайней мере switch-выражение основной конструкцией сопоставления с образцом, и последующего соответствующего вычисления выражения. Но такому оператору конкуренцию в C# будет составлять не swithc case инструкция, а тернарный оператор "? :", позволяющий писать тоже вполне лаконичные и универсальные ветвления выражений. Но switch будет предпочтительнее в очень больших инструкция ветвлении, а так же во всех случаях прямого и явного сопоставления с образцом! А многие операции не тривиального ветвления (при вычислении выражений) могут быть приведены к сопоставлению с образцом. И такой подход - это современная навязываема тенденция (с корнями из функционального программирования).

Кстати, например в ЯП Nemerle - паттерн-мэтчинг (через оператор match) был основным оператором ветвления. Остальные операции ветвления были синтаксическим сахаром, построенном на макросах вокруг macth. Тем самым - операция сопоставления с образцом - самая универсальная.

Замечу ещё то, что компиляторы могут ещё и более эффективно оптимизировать операции сопоставления с образцом, чем простое ветвление. Но не знаю как тут насчёт оптимизации на уровне микрокода современных процессоров!

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

Отказались от модификатора по умолчанию для членов класса (private). То есть явно его указывали.

Это что первое на ум пришло. В регламенте было больше пунктов. Цель - повысить читаемость кода.

Честно говоря, не понимаю негодование насчёт синтаксиса switch-expressions... Тут дело вкуса. Кому-то нравится, кому-то — нет. Если Вам не по душе такой код, Вы спокойно можете его переписать в привычном синтаксисе с case.

Проблема в том, что программист редко работает в вакууме. Лично я, да могу не использовать нововведения. Но они всё равно появятся в моём коде, кода я начну копипастить со стековерфлоу или править код товарища по команде, пока он в отпуске и т.д. и т.п. Т.е. страусиная позиция — не прокатит. Я так уже пытался с дженериками в джаве, когда их только ввели.
Сам лично против такого свитч особо ничего не имею. Но меня расстраивает общая тенденция превращения С# в «свалку концепций» наподобие С++. До такого маразма ещё не дошли, но явно стремятся.
В той же джаве — тоже наваливают, но хоть как-то стремятся сохранить «генеральную линию». Например, класс — всегда класс. А для смены концепций появляются всякие скалы и котлины.
С С# — тоже пытались, но VB, F# и проч. — скорее мертвы чем живы и теперь всё валом валится в С#. И теперь, продолжая пример, там «типов классов» штук пять или шесть уже.

Частенько начал замечать, как какой-нибудь бывший C# разработчик свичнувшись на Go обязательно считает своим долгом в комментах пожаловаться на то, как ему было неудобненько писать на C#(обязательно с примерами) и как его жизнь кардинально изменилась в лучшую сторону. Это обязательное условие для перехода на Go? Просто вдруг если тоже свичнусь, то придется в комментах .net помоями обливать...))

А зачем для сортировки теперь два разных метода? Можно было сделать один Order, который без аргументов ссылается на себя, а с аргументом аналогично текущей реализации OrderBy. Это какая-то дань обратной совместимости или под капотом разные реализации?

Это LINQ, там под капотом всех методов одна и та же реализация - вернуть новое дерево выражений с довешенным сверху method call. Реализацией и разбором что делать с каждым вызовом занимается конкретный LINQ Provider. Существующие провайдеры при виде method call с тем с именем метода OrderBy, но без аргумента будут падать.

Они, в принципе, и при виде просто нового Order будут падать, но, по крайней мере, с конкретной ошибкой что метод не поддерживается, а не с NRE. Так что это все ради обратной совместимости.

Нет не одна. Дерево только для провайдеров IQueryable. Для IEnumerable там обычные функции

Но синтаксис вызова то должен для обоих случае быть единым!

Если честно - не понял, что это значит.
Я мне видится, что эти Order добавили по аналогии со старыми Min/Max, когда, например, коллекция примитивов и нет смысла писать OrderBy(x=>x).
Ну или внутри класса есть описание для сортировки

Я имею в виду не хорошо делать разный API для Entity framwork и для объектной базы. Поэтому приходится вести всё под одну гребёнку - невзирая на разную реализацию для обслуживания различных интерфейсов

В смысле единым? У них сигнатура разная. В одном случае передаются делегаты Func/Predicate, в другом выражения Expression.

В IQueryable можно кстати вызвать методы IEnumerable. Сразу произойдёт материализация запроса, сколько я понимаю

Для меня самая жирная фича -- Support polymorphic deserialization

Такая плотная интеграция с докером — сомнительная вещь.
Всё-таки докер вещь самостоятельная (Микрософт их не купили? Я не в курсе) — кто знает что и как там сложится. И такая фича может повиснуть очередным «мёртвым легаси».

Кто знает) Я тоже не до конца понял суть этого нововведения, но мб кто сможет пояснить за эту тему...

А про какую плотную интеграцию речь-то? Судя по примеру, просто добавили профиль публикации (PublishProfile, см. pubxml). Это по сути кастомный набор правил и команд для сборки и dotnet publish знать не знает, что этот DefaultContainer делает.

Немного нытья про линукс)))
Когда наконец будет настоящая кроссплатформенность? Как пользователь линукса dotnet выглядит ужасно: нельзя установить больше 2-х sdk, sdk от net6 не может работать с проектами для dotnet3.1. Приходится использовать mono. Кроссплатформенного gui пока официально не завезли. Благо откопал свежий проект на OGL (lxui). Нововведения выглядят как попытка прихватить рынок JS и ускориться до C. Посмотрим что будет с шарпом дальше))
PS: помогите с репой плиз))) хочу статьи писать

Sign up to leave a comment.