Pull to refresh

Comments 31

Очень круто. Разве что я всегда думал что шарпам в частности и дотнету в целом не достаёт строгости, и фичу с дефолтной реализацией в интерфейсах в C# 8 я воспринял с опаской. Тем паче всегда с ужасом и восхищением я смотрел на то как люди реализуют вещи вроде описанных в статье для чего-то кроме спортивного интереса.
Да поможет господь тем, кому придется поддерживать код на ваших трейтах.
А если серьезно, то очень хотелось бы подробнее узнать какая проблема/задача ( наверняка интересная и нетривиальная) привела к созданию вот этого.

Всё гораздо более тривиально чем кажется — круды, много крудов, очень много крудов. В определённый момент начинается дежавю, что всё уже где-то было сделано и хотелось бы проехаться на уже реализованных фичах.
Работа интересная, но такие «велосипеды» я бы поостерегся в продакшен пихать. Когда первый раз читал про SG, так и думал, что этот достаточно мощный инструмент будут пихать куда нужно и не нужно. Множественного наследования принципиально в язык не добавляли, этому способствовали многие причины, и если каждый будет делать что-то на коленке и добавлять в реально работающие проекты, ломая основной посыл C# причем совершенно кустарным способом — ничего хорошего не будет. Я так в одной компании один день проработал — у них там 70% кода c# проекта просто генерировалось через самописный DSL, возможность быть спецом именно по этому языку, который дальше их компании не ушел (и никогда не уйдет) — не очень перспективная тема.

Сам я рассматриваю и допускаю использование SG в своих проектах только в контексте генерации некоторых моделей для/из контрактов и как некую замену Reflection в плане увеличения производительности.
Полностью согласен, поддерживать чужой велосипед, не большое удовольствие. Вот писать свой — другое дело.

Но честно говоря не очень понятно за что вы голосуете — писать всё на чистом C#? Или использовать только широко распространнёные библиотеки? До того как они стали широкораспространнёными, тоже делали MVP и смотрели — взлетит идея или нет. Не сразу же все знаменитыми становились, ну или по крайней мере у них был бюджет, чтобы потратить на развитие.

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

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

идеальной стадии для экспериментов

Microsoft выпустили джина из бутылки. Это очень мощный инструмент, неправильное применение которого на стадии экспериментов может превратить в будущем проекты в совершенно не поддерживаемое легаси.
И что до вопроса по C#, да я бы предпочел в будущем проекты, которые используют стандартные и широко зарекомендованные приемы, чем велосипеды, только в которые требуется дополнительное длительное погружение, а не в предметную область бизнеса.
Вот второй случай я и считаю самым неудачным для проекта в целом.
Я может быть скажу крамольную мысль, но не все разработчики заботятся о том что «лучше» для проекта, особенно если проект сам по себе унылое г. И придумывают себе немного развлечений в виде написания инструментария, велосипедов и т.п.

Тем более что «лучше» для проекта — оно у всех разное, не дай бог ещё бизнес спросить, что же лучше, много нового для себя можно открыть.
Это очень мощный инструмент, неправильное применение которого на стадии экспериментов может превратить в будущем проекты в совершенно не поддерживаемое легаси.
Опять же, не понятно какое у вас предложение? Запретить и не пущать? Понятно что будут рождены уродливые мутанты (вполне возможно что и этот проект один из них), но это как раз вполне ожидаемо.
да я бы предпочел в будущем проекты, которые используют стандартные и широко зарекомендованные приемы, чем велосипеды
Ну тогда надо ещё пообещать самому не баловаться, а использовать только «стандартные и широко зарекомендованные приемы».
а не в предметную область бизнеса
boooring…
пообещать самому не баловаться,

Сейчас так и делаю, смотрю кто решал те или иные проблемы на github, развивается ли проект и т.д. и внедряю по возможности.
boooring

Ну не знаю, мне вот в бизнес интересней вникать, чем в очередную новомодную библиотеку. Точнее интереснее когда применение некоторых практик помогают эффективней решать проблемы бизнеса.
Сейчас так и делаю, смотрю кто решал те или иные проблемы на github, развивается ли проект и т.д. и внедряю по возможности.
Очень часто такое приводит к эффекту «leftpad-a».

Когда в проект добавляется миллиард зависимостей из-за самых простейших вещей. Особенно с .net core это очень заметно, становится похожим на nodejs.

Сложно отслеживать зависимости, качество и безопасность этих проектов.

Недавно обсуждали с коллегами идею сделать nuget для исходников, т.е. проекты меньше определённого размера тянуть не бинарниками, а исходниками. И публиковать их в формате, чтобы можно было брать нужные части, а не «all or nothing». Т.е. ты становишься владельцем симпортированного кода и отвечаешь за него.
Ну не знаю, мне вот в бизнес интересней вникать, чем в очередную новомодную библиотеку.
Все люди разные, я например повникал достаточно, чтобы бросить менеджерство и обратно разработкой заняться.

Но всё равно постоянно пытаются навесить сверху. Какое-то проклятие профессии, как будто в свой области мало сложности.
Как раз если будут исходники будет полный «leftpad», а пока это так ерунда.
От того, что будут исходники отслеживать качество и безопасность ничуть не проще.
Кроме того любую dll можно dotPeek-ом превратить в полноценный проект C#, а потом добавить в проект как исходный код.
Но зачем ?:)
UFO just landed and posted this here
К сожалению это всё не работает (пока?) с record-ами…
Как говорит моя дочка — «easy peasy lemon squeezy», при желании прикручивается в полпинка.

А зачем нужен атрибут TraitIgnore? Можно же просто было делать метод в трейте абстрактным, всё равно абстрактные методы в самих трейтах не нужны (то есть, копировать их в целевой класс не надо).

Приватные методы/свойства нельзя объявить абстрактными, для protected конечно можно.

Мы думали над подобным, но решили что явное объявление более правильный подход, чем подразумеваемое правило. Немного более многословный, но лично я бы предпочёл так. Хотя конечно можно обе версии добавить. т.е. игнорировать и abstract в дополнение к TraitIgnore.

Ну так не делать их приватными :) Они всё равно нужны только для того, чтобы удовлетворить компилятор/IDE, ведь пользоваться ими и так никто не будет. Зато не надо будет писать вот эти заглушки (правда, в вашем варианте можно ещё исключение просто выбрасывать):


    [TraitIgnore]
    private string GetNamePrefix()
    {
       return null;
    }

Но с абстрактным методом, мне кажется, код выглядел бы чище.

Спасибо за обосование, поговорю с коллегами, скорее всего прикрутим.

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

Отмечу, что для множественного наследования, когда нужны только методы, вполне достаточно использовать C# 8 default interface methods.


Лично мне в C# пару раз очень пригодилась бы реализация traits как в rust, без необходимости модифицировать класс, чтобы их можно было применять к существующим типам, например из готовых библиотек.
Чтобы к классам прикреплялся интерфейс трейта, и под ним их можно было использовать как параметры в методах или обобщениях.

Можно добиться похожего поведения, если явно принимать реализацию типажа в методе:


interface ITrait<T>
{
  void TraitMethod<T>(T value);
}

class External
{
}

struct ExternalTrait : ITrait<External>
{
  void TraitMethod(External value) {}
}

class Consumer
{
  void Consume<T, TTrait>(T value) where TTrait : struct, ITrait<T>
  {
    default(TTrait).TraitMethod(value);
  }
}

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


class Extensions
{
  public static void Consume(this Consumer consumer, External value)
  {
    consumer.Consume<External, ExternalTrait>(value);
  }
}

В целом не очень красиво, т.к. на стороне потребителя нельзя просто написать value.TraitMethod(), тем не менее у меня в одном проекте такой подход прижился.


P.S. Промазал, это ответ для SIDOVSKY

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

UFO just landed and posted this here
По-моему с методами расширения неудачный пример. Я тоже запилил такие методы для удобства чтения кода:

((IMyInterface)myObject).Field1

myObject.To<IMyInterface>().Field1

var stringArray = new [ "a", "b", "c"];

string.Join(", ", stringArray);

stringArray.Join(", ");

public void MyMethod(IList<int> list)
{
  if (list != null && list.Count > 0)
  {
    // ...
  }
}

public void MyMethod(IList<int> list)
{
  if (list.IsNotNullOrEmpty())
  {
    // ...
  }
}
UFO just landed and posted this here
А что не так с кодом?
null при сравнении всегда даёт false.
А .Count стал возвращать int? вместо из-за list?..
UFO just landed and posted this here
доселе невиданные методы появились у стандартных библиотечных классов

Посмотрит исходники или документацию.

расширения своих собственных классов

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

Насколько я понял идею методов расширения, они используются для:
1. Добавления новых (более удобных) методов для уже закрытых для изменения классов.
2. Для массовой реализации нового поведения для заданного интерфейса. Т.е. чтобы не прописывать в каждом классе реализацию, можно сделать метод расширения на интерфейс и все классы наследники получат это поведение. Мне это напоминает реализацию интерфейса по умолчанию, которое появилось в C# 8.0.
Я с ужасом жду каждой новой фичи языка, потому что уже знаю, что первое же для чего она будет использована, это создание содома и гомморы.
Ну когда что-то новое появляется, начинают экпериментировать и конечно могут от энтузиазма немного переборщить.

Экпериментировать конечно лучше на маленьких проектах, но я вполне себе допускаю что писатели из вашего примера, не считали своё изделие «содомом и гомморой» — потому что это была их «содома и гоммора», также как очень вероятно, что новый человек пришедший на ваш проект, потихоньку, за глаза рассакзывает какой-же «содом и гоммору» творит fkthat, а вы искренне гордитесь выбраному подходу, потому что это ваш «содом и гоммора».
UFO just landed and posted this here
Ошибка компиляции CS0102 или CS0111.
В этом один из плюсов подобного подхода, когда из кусков собирается целое, а не реализуется честное множественное наследование. В результате получается стандартный класс и при наличии проблем, он упадёт при компиляции, а не будет пытаться разрулить дополнительными правилами.
Sign up to leave a comment.

Articles