Pull to refresh

Comments 48

А где в последнем списке generic-и?

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

А Вы точно C# активно пользовали до Go?

Странно, у меня почти везде generics заменили интерфейсы...

интерфейс есть контракт, можно поинтересоваться как у вас дженерики его заменили?

UFO just landed and posted this here

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

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

Вот старый-старый тред на этот счёт на RSDN: http://rsdn.org/forum/philosophy/2853873

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

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

Хорошая статья, подробная. Да и владеть двумя достаточно современными ЯП - тоже круто.

Хммм... Обмельчали нынче разработчики... 2-3 "взрослых" языка в активном использовании, ещё пяток о которых есть представление либо не самый свежий опыт, плюс десяток вспомогательных языков -- не это ли обязательный набор уважающего себя разработчика?

где следует объявлять интерфейс: в пакете, в котором он реализуется, или в том, в котором используется? Правильный ответ — второй. Нашему коду требуются от типа определённые методы, и мы явно это указываем. Если объявить интерфейс в другом пакете, то получится ненужная зависимость, при обновлении которой может сломаться код.

Это, скажем так, весьма небесспорное утверждение. Есть общепринятая присказка "accept interfaces, return structs", однако она никак не регулирует место, в котором интерфейсы определяются. Кроме того, если вы какие-то методы используете, то зависимость получается как бы всегда, и при обновлении пакетов код может сломаться в любом случае, если сигнатуры поменялись. А от того, что вы формально расширили свои зависимости от минимально необходимых до минимально предоставляемых, компилятор не помрет.

С другой стороны, когда пакет явно и в одно месте определяет интерфейсы, которые он предоставляет - это гораздо удобнее чем искать все публичные методы типов в этом пакете. Так что мы, например, пакеты пишем как-то так:

type Smth interface { ... }
type impl struct { ... }

func NewSmth (...) Smth {
  ...
  return impl{...}
}

func (i impl) Func1(...) {
  ...
}
...

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

Более того, выкусывая подобным образом методы из типов, вы ломаете всю иерархию зависимостей (в плане типов языка, не в плане пакетов/модулей) у себя в приложении, что может стрельнуть неожиданным образом, начиная от DI-штук, кончая навигацией по коду ("таак, где у нас этот тип используется? эээ, ой").

Конечно, не стоит все воспринимать как догму, вполне допустимо использовать подобным образом всякие простецкие интерфейсы из одного-двух очень общих методов, типа Stringer, Reader, ReadWriter итд.

Пишу на C#. Щупал Go. ИМХО, для Go больше подходит для системного программирования и для каких-нибудь фоновых обработчиков. На данный момент для Web API все же C# предпочтительней, благодаря более развитому ООП. Ну и наличие продвинутых фреймворков и библиотек перевешивает чашу весов на сторону шарпа.
Под солнцем всем языкам хватает места. Благо есть выбор - под разные нужды свой язык.

На данный момент для Web API все же C# предпочтительней,

Web API - это же вы rest имеет ввиду? В целом на го написать бекэнд можно, но наверное самая большая проблема в том, что полноценных фреймворков уровня Spring нет и многие вещи приходится писать самому. Соответственно требования к разработчикам и времени на проект возрастают, что просто невыгодно. Поэтому го часто используют для каких-то отдельных кусков проекта.

благодаря более развитому ООП

На мой взгляд гошного ООП вполне достаточно для этих целей, т.к. веб в большинстве случаев не требует каких-то слишком сложных абстракций. У питона ООП не сильно развитое, но там есть удобный фреймворк, которой позволяет быстро вести разработку, что довольно быстро сделало его популярным в этой сфере.

Go больше подходит для системного программирования и для каких-нибудь фоновых обработчиков.

Он подходит для этого и на го пишут довольно много интересного софта, но 95% реальной работы связано с сетью и в основном с http.

P.S. Если не сложно, то расскажите, с чем вы поработали(библиотеки, фреймворки) пока изучали го, просто интересно.

Тут речь скорее об enterprise с большим доменом, у C# есть EntityFramework для этого, исключения, generic (с недавних пор и в go), MediatR, Automapper и многое другое. В go всё таки сложнее работать с доменами

с другой стороны, наличие MediatR может служить индикатором что на этот проект устраиваться не надо ))

Потому что никто доходчиво не может объяснить, какую проблему он решает, усложняя навигацию по коду

Вы знаете, я почему-то всегда считал, что он упрощает навигацию по коду. Можно сказать, что это одна из проблем которые он решает )
Можно попросить вас сделать один эксперимент, конечно если есть время и желание...
Скачайте репозиторий Джейсона Тейлора https://github.com/jasontaylordev/CleanArchitecture
Откройте контроллер TodoItemsController, в 22 строке будет объявление метода
"public async Task<ActionResult> Create(CreateTodoItemCommand command)" кликните Сtrl+Click по команде (я ожидаю действие Go To Definition). Вы должны сразу увидеть тело команды и обработчик. Разве это не простой способ в один клик попасть в код бизнес-логики? Может в проектах где вы работали команды и обработчики были сильно разнесены?

Это проще, чем кликнуть ctrl+alt+click (перейти к реализации) на условный _createItemService.Execute()? Всё таки, изначально нет проблемы навигации по ходу. Я, кстати, впервые слышу, что это проблема, которую решает медиатр)

Вообще, приходится пихать и команду и обработчик в один файл. В целом, это скорее антипаттерн, чем нормальное решение, хотя и мы так делали.

Плюс, часто логика условных Create\Update часто пересекается на ~90%, что приводит либо к копипасте, либо к handler'у для более чем одной команды. Какой вариант предпочтительнее? Или есть ещё варианты?

буквально недавно на несколько каналах разработчика MAUI и веб сервисов, показывали, как с помощью MediatR делать и дестопные приложения или использовать его в miniAPI, что как бы дает намек что библиотека очень даже крута и ей надо пользоваться. может вы неправильно готовили её?

EF, MediatR, AutoMapper, DI, побольше рефлексии для входных аргументов обработчика (например, для валидации) и ваше приложение отвечает 200мс на обычный crud. Не для всех приложений такое позволимо, но об этом не задумываются при разработке MVP. "железом закидаем", "больше времени в БД проведём". Проблема c# как раз в таком подходе - слишком развязаны руки у разработчиков. Слишком хочется пользоваться уже написанным, не зная как оно работает внутри. Поэтому на Go и переходят в хайлоад проектах, как мне кажется.

Go больше подходит для системного программирования

Не очень согласен - системное обычно там где нет рантайма, т.е. прямо делаем код который будет работать на голом железе. Возможно с Rust спутали. Хотя если считать k8s или Docker системами, то наверно может быть.

На шарпе я проработал два с половиной года.

Вы не обижайтесь, но за такой промежуток времени вы не можете проникнуться никаким языком. Да, пробежать по основным принципам - можно. Ткнуться в пару вещей - да, это вполне возможно, но чтобы так громко заявлять, что вы с шарпа на go переходите - это не совсем отражает то, что на самом деле произошло. А произошло следующее. Вы занялись шарпом. И через какое-то время перешли на другой язык. И теперь вам захотелось этот ваш шаг 'официально' подтвердить, что вы всё сделали правильно. Конечно-же найдётся в go что-то, что делается или лучше или проще. Вы на это и указываете. Имеете полное право, но шарп огромен, чтобы вот так на двух примерах его оценить - как-то однобоко.

Вы совершенно верно подметили посыл статьи: "В каждом языке есть то, что делается или лучше, или проще, чем на другом." Моя статья обзорная, потому что различий и общих мест у Go и C# масса. На их описании можно составить целую книгу. Если бы у меня была возможность, я с радостью написал бы что-то фундаментальное вроде "C# vs Go".

Я вас понимаю, но немного приведу пример. Давайте поменяем шарп на французский язык, а go на английский. Итак ваш рассказ звучит след. образом.

Как только я приехал во Францию, то сразу начал изучать французский язык, но почти сразу-же записался и на курсы английского языка. Вот уже прошло два с половиной года, я закончил курсы английского, нашёл работу, где говорят только на английском языке и всё- я решил поехать жить в Англию. И вот вам пример, что английский лучше, чем французский. Вы только представьте как говорят на французском число 90 - это quatre-vingt-dix . Это значит 4 раза по 20 + 10. С ума сойти можно. А теперь посмотрите как это классно и лаконично на английском - ninety.

Думаю вас это улыбнуло. Вот так-же это воспринял и я, после вашего рассказа.

60, 70, 80, 90 и другие числа во французском может быть и правда не очень практично сконструированы, но от этого песни Эдит Пиаф, Шарля Азнавура, Джо Дассена, Мерри Матьё, Патрисии Каас, Alizee, Zaz и многих других от этого ведь не хуже.

"Go лучше шарпа" - не тезис моей статьи. Пример с языками конкретный (на английском быстрей считать десятками), а выводы - огромные ("английский лучше, чем французский" и французская музыка хуже английской). По аналогии можно вывести, что из-за многознаковости римских цифр латинская литература хуже арабской.

за такой промежуток времени вы не можете проникнуться никаким языком

Что значит "проникнуться"? Знать детали реализации рантайма? Понимать как писать идиоматичный код?

И из вашего же сообщения:

вы с шарпа на go переходите - это не совсем отражает то, что на самом деле произошло

А произошло следующее. Вы занялись шарпом <...>, перешли на другой язык

то есть, человек перешел с шарпа на го. В чем автор не прав?)

Что значит проникнуться? Ну скажем так - когда мы говорим шарп, то уверен, что говорим не только о структуре и грамматике языка - это обьясняется быстро. Ну заучивается тоже в нормальное время. Мы говорим о шарпе и о многих бибоиотеках, которые нужны для работы в различных сферах. О различных структурах данных и работе с ними. Когда я вижу пример, где вставляется delay, не важно где - шарпе или в с++ для микроконтроллера, то почему-то думаю, что это не продвинутый подход к решению вещей. А именно начинающий. Может быть я ошибаюсь. Вполне.

Насчет прав/не прав. Автор во всем прав. Если он считает, что он спец по шарпу и перешёл на го - его право. Если он решил об этом сообщить - тоже его полное право. Моё же право считать, что за небольшой отрезок времени, в те два с половиной года, где он каждый день больше занимался на курсах с го, а не интенсивно утонул в шарпе, что в шарпе он не мог стать спецом. Конечно, талант никто не отменяет. В жизни всякое бывает. К автору не отношу. Но бывает и такое, когда, книжки типа ,шарп за 21 день' дают людям представление, что это и правда возможно. Но придерживаюсь правила, что для познания каких либо знаний, как в человеческих языках, так и в языках программирования, в карате или в шахматах, в физике или в математике итд. нужно полное погружение, уйма проб и ошибок и время. Много времени. Может быть и тут ошибаюсь. Кто знает?

Ладно - останемся каждый со своим мнением.

Делал как-то гошный проект на работе

Сходу для себя ощутил острую нехватку перегрузки функций и опциональных аргументов функций

В чатике по го мне сказали что я ошибся языком, ¯\_(ツ)_/¯

Что эти ваши дженерики, тут бы простой код писать без кучи дублирований

Для себя я не нашёл жизни на го после шарпов

Когда переезжал с php на go были такие же ощущения, но со временем это развеивается и с идиоматикой go начинаешь писать по-другому. Не стоит привязываться к конкретным фичам яп, это всего лишь инструмент. Та же аналогия из комментария выше - "я не говорю на французском из-за того,что там нет нескольких слов, которые мне нужны/нравятся". Попробуйте переосмыслить то, что используете на вашем яп и как это можно спроецировать на другой, откроете для себе много нового и интересного. Да, это выводит мозг из зоны комфорта, но это определенно улучшает восприятие и понимание того, что вы делаете.

Хочется отстоять романтику своего утверждения:

Я завязываюсь не на фичи языка, а на концепции, реализуемые языком

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

Но плохо не потому, что это есть, а плохо потому, что мне, как программисту, это недоступно

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

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

В своих эмпирических доводах опираюсь на подобную статью подобного рода:
http://rsdn.org/article/nemerle/Amplifier.xml

P.S. на практике не ограничиваюсь одним языком

Не то, чтобы я сильно топил за Go, но я рад, что есть ЯП без перегрузок функций.

Чем не устраивает перегрузка функций?

Тем, что затрудняет анализ ошибок в рантайме. И повышает требования к разработчику.
Я вообще, знаете ли, жду появления процедурного ЯП с простым синтаксисом, с небольшим набором инструкций, с простыми структурами данных. Вся эта избыточная мощь (С++), лапша ООП и прочие UB уже немного надоели.
Хочется понимать происходящее в месте где читаешь код, а не держать в голове кучу влияющих сайдэффектов.

Ассемблер что ли?

UFO just landed and posted this here

А ещё перегрузки нету в Си (который без плюсов).

Согласен с комментатором, и добавлю что C# почти на 10 лет старше Go, и не факт что через 10 лет словарь Go будет такого же размера. Языки развиваются.

Думал о полном переходе с шарпа на go несколько месяцев назад. Благо, писал в свое время на разных языках и go активно пиарили. Сделал пару pet-проектов, чтобы хотя бы примерно понимать, что это такое и с чем его едят, и отказался от этой затеи. Взяться за разработку чего-то масштабного на go не решился бы. Понятно, что язык проектировался под высоконагруженные масштабируемые crud'ы, но реализовывать там сложные алгоритмы работы с данными не стал бы.

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

Однако многопоточная обработка с обменом сообщениями между потоками на Go пишется быстрее и получается лаконичнее. 

А можно поподробнее про практическую часть этого утверждения? По собственному опыту современный шарп выстроен таким образом что тебе в принципе нету необходимости заниматься перегонкой данных между потоками, там либо потоки так организуются что этого не требуется вовсе в 99% случаев, либо используются конкурентные коллекции которые решают оставшийся 1% случаев.

На практике также не приходилось использовать каналы в шарпе. Однако они есть в языке, а статья основана на сравнении C# и Go. Поэтому пример с каналами не мог не привести. К тому же он отлично подтверждает тезис: "На C# можно решать любые задачи, причём зачастую несколькими способами."

Спасибо за статью, почти узнал себя. Мой опыт очень похож на ваш, по применяемым технологиям и проектам. Но меня вот после прохождения ozon route 256 по направлению C# на собеседование не пригласили(( Есть уже мысль пробовать поступать на направление по GO, вдруг заметят)

Всегда можно откликнуться по прямой ссылке на вакансию. Или открыть резюме на HH - рекрутёры Озона не пройдут мимо. Я именно так и оказался здесь. После заваленных собеседования и двух скринингов...

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

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

Сравнение совсем некорректное.

func generate(count int) chan string {
   ch := make(chan string)

А вот так это будет выглядеть на C#:

public static ChannelReader<string> Generate(int count)
{
    var ch = Channel.CreateUnbounded<string>();

А это ни разу ни одно и то же.

  1. Channel в C# это не тупой канал, это умный инструмент, с настраиваемым размером буфера, различными стратегиями при переполнении буфера

  2. Channel сам по себе асинхронный, т.е. само чтение или запись не требует ещё одной "горутины"

  3. Поддерживает кооперативную отмену

  4. Содержит различные способы записи, чтения, и просто детекции событий в канале

В целом, "уход на Go" мне видится не как смена технологического стека, а скорее смена места работы.

GoLang прекрасный ЯП. Но чисто технически и практически (без учёта зарплат, и безумного хайпа), у GoLang совершенно нет никаких преимуществ перед .NET, это если конечно не упарываться сравнением, у кого там меньше/больше ключевых слов :)

Sign up to leave a comment.