×

Явная типизация C# VS var

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Возник камень предкновения в плане конвеншна именования переменных уровня методов.
Народ предлагает явно не указывать типы,
вот новомодные конвеншны C#6.0 диктуют свободу и var’ы — msdn.microsoft.com/...​-us/library/ff926074.aspx
Линка пишет — прописывайте тип явно, где согласно контексту неочевидно, каков тип.
Считаю, суть и удобство шарпа — явная типизация, var’ы иногда допустимы, но не в crud’ах.
Народ, ваше мнение?

я считаю, надо так:

      public void Update(EntityViewModel entityVM)
        {
            Entity entity = MapToPoco(entityVM);
            _entityRepository.Update(entity);

            DateTime oldDate = entity.Date;
            DateTime newDate= Convert.ToDateTime(holidayVM.Date);

           _entityService.UpdateEntityWithDates(oldDate, newDate);
        }

предлагают так:

        public void Update(EntityViewModel entityVM)
        {
            var entity= MapToPoco(holidayVM);
            _entityRepository.Update(entity);

            var oldDate = entity.Date;
            var newDate= Convert.ToDateTime(entityVM.Date);

           _entityService.UpdateEntityWithDates(oldDate, newDate);
        }

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

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn

Найкращі коментарі пропустити

Я рекомендую придерживаться соглашений MS Coding Conventions в этом вопросе. То, что прописано в разделе «Implicitly Typed Local Variables» по Вашей ссылке появилось там не просто от нечего делать.
1) В случаях, когда тип явно не следует из правой части использование «var» создает ряд проблем и не рекомендуется. Это не только заметно ухудшает читабельность кода, особенно чужого, но и может привести к неожиданным багам. К примеру, у меня в практике был случай, когда в гриде клиент попросил добавить новый столбец и присуммировать его значения в Total. Изначально код имет такой вид:


var totalCellValue = GetBoY() + GetContributions() - GetDistributions();
После добавления еще одной колонки код стал выглядеть так:

var totalCellValue = GetBoY() + GetContributions() - GetDistributions() + GetCorrections();
Код скомпилировался без проблем, а все значения возвращаемые GetCorrections() были равны «0». Вот только при проверки выяснилось, что до внесения изменений все значения в колонке «Total» были отличны от нуля, а после добавления GetCorrections(), все значения в колонке «Total» стали равны только «0».
Причина бага была в том, что первые 3 метода возвращали «decimal», а вот новая колонка возвращала «decimal?». И хотя null там никогда не возвращался, но тип totalCellValue также в результате незаметно поменялся с decimal на decimal? и при скармливании этого значения в грид последнему сносило крышу и он просто рисовал «0» вместо реальных Value.
2) В случаях, когда тип явно следует из правой части или в циклах for/foreach, использование var вполне нормально — это несколько ускоряем написание кода и не сказывается негативно на его читабельности. Использовать или нет явную типизацию в данных случаях — это вопрос соглашений внутри команды — тут гораздо хуже, если разные люди в команде будут использовать разные подходы. Т.е. тут как договорились в команде — так и делайте, главное договориться и делать одинаково.

Если отталкиваться строго от соглашений Микрософта, то Ваш пример должен выглядеть так:


public void Update(EntityViewModel entityVM)
{
// Из правой части тип не ясен, поэтому var не рекомендуется.
// Это единственное место, где я не соглавен в Вашей тимой.
Entity entity = MapToPoco(entityVM);
_entityRepository.Update(entity);

// Допустимы оба варианта. Здесь тип очевиден из правой части.
// Явное объявление потребуется, если в entity.Date не DateTime
var oldDate = entity.Date;

// Допустимы оба варианта. Явное объявление типа не требуется.
// ToDateTime не оставляет сомнений в том, какой тип вернется
var newDate= Convert.ToDateTime(holidayVM.Date);

_entityService.UpdateEntityWithDates(oldDate, newDate);
}

Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Кстати, вот соглашения по var в команде ASP.NET Core github.com/...#usage-of-the-var-keyword. Они используют var везде где можно.

В этом споре всегда интересовал такой вопрос: а не пофигу ли переменная какого типа вернется? Что важно так это те поля и функции которые она предоставляет. А имя класса этого все равно не описывает.

Этому «новомодному» конвеншену уже почти 10 лет, неявный вывод типов появился в C# 3.0 в 2007 году :)

Нормальне питання.
Власне, треба слідувати стилю прийнятому в команді, навіть якщо не погоджуєтесь.
Але можна при цьому ставити питання про прегляд стилю.
Ми користуємося ось цим принципом, з я

прописывайте тип явно, где согласно контексту неочевидно, каков тип.
Легше робити code review, читати, аналізувати історію.
Тому що ми ж знаємо, що «Programs must be written for people to read, and only incidentally for machines to execute»
явная типизация
а тут здається путанина з розумінням як працюють типи в C#

тут скорее путаница понятий статическая/динамическая и явная/не явная

Мой вам совет — удалите эту тему, если это возможно

Зачем? Весь смысл доу в срачах X vs Y.

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

Yaroslav Ulanovych ушел гуглить свое имя.

К вам уже выехали собеседовать :)

В этих ваших интернетах порой сложно понять кто серьезно, а кто прикалывается.

Прикалываться под своим именем не стоит :)

И что? Нормальный здравый вопрос. Топикстартеру это только в плюс, тем более показывает его интерес к профессии, а не просто к баблу.

Это не показывает ничего, окромя того топик стартер склочная женщина.
Вместо того что бы слушать и следовать договоренностям команды у нее появляется особое мнение по глупейшему вопросу. Неважно, пишите ли вы var или пишите имя метода. Оба подхода имеют свой смысл в зависимости от контекста. Еще раз повторюсь, это не есть важно. Важно есть то, что есть договоренности внутри команды и им надо следовать. Логично было бы разобраться самостоятельно или обсудить с командой автор лезет на DOU. Порождая традиционную для ресурса полемику.

Вместо того что бы слушать и следовать договоренностям команды
вместо того, чтоб подчиниться и скрипеть зубами, вышел поворчать в сообщество.
либо сообщество подавит(и тогда ничем не хуже начального варианта), либо убедит(человек получит новые знания), либо подкинет аргументов достаточных для продолжения спора уже в тиме(прокачка скиллов).
в итоге ни человек, ни команда не проигрывает. а может и получить профит.
Логично было бы разобраться самостоятельно или обсудить с командой автор лезет на DOU
откуда у вас взялось «или»? неужто, в вашем опыте, любой задавший вопрос на ДОУ/SO/etc уходил в добровольное изгнание до момента закрытия темы?

Одна из китайских поговорок гласит :
«Внешними практиками занимаются только упрямые дураки»

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

Пока что вы показали только то, что мачо с претензиями.

Неважно, пишите ли вы var или пишите имя метода. Оба подхода имеют свой смысл в зависимости от контекста.

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

Логично было бы разобраться самостоятельно или обсудить с командой автор лезет на DOU.

Я уверен на >50%, что эти шаги были сделаны и результат показался недостаточным.

Отдельная интересная тема с методами, который возвращают таски. Скажем, метод стал асинхронным. До рефакторинга переменная с результатом выполнения метода под var содержала Entity, а после — Task<entity>. И становится ой как неочевидно почему оно работает не совсем так, как ожидается.

Если бы типизация переменной была явной получили бы ошибку этапа компиляции и было бы очевидно, что нужно добавить await.

Никто ведь не знает наперед станет ли какой-то метод асихронным в будущем.

Странный у вас рефакторинг при котором метод становиться асинхронным и не меняет своего названия с *() на *Async().
С таким надо бороться не запретами на использование автовыведения.

Эм, не понял про название. Допустим поменял. Но мы же не вручную меняем. Для этого есть R#. Кстати, мы на проекте недавно поменяли конвенции — решили отказаться от постфикса Async. И мне, скорее, нравится, но это дело привычки. В JS медоды, возвращающие Promise называются так же, как и все остальные. Т.е. там нет такого различия. И тем более нет смысла иметь синхронные реализации асинхронных методов. Так что Async это, можно сказать, legacy для обратной совместимости API и, думаю, постепенно уйдет. Но мне самому решение отказаться от Async далось со скрипом.

Кстати, мы на проекте недавно поменяли конвенции — решили отказаться от постфикса Async.
И кто вам доктор?

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

А что раскрывать? Отменили удобное соглашение — получили проблему.

Я не говоил, что проблему получили из-за соглашения об Async. Проблема может возникнуть из-за ипользования var вместо явной типизации. О чем собственно, и топик.

Если бы не отменили соглашение об Асинках — проблема использования вара для результата асинхронных методов не поднялась бы.

Если бы топик написали до того как мы поменяли соглашение я написал бы точно такой же комментарий. Проблема не в том, что у разработчика не возникнек вопрос «Где await?» если у метода не будет Async в конце (c этим R# помогает, если конечно клиентский метод тоже помечен async). Проблема в том, что код продолжит компилиться, если возвращамое значение метода поменяется в целом, и с T на Task<t> в частности, а при явной типизации будет ошибка компилатора в нужном месте. Дополнительный уровень безопастности и всего-то.

Var имеет такую же безопасность типов как и явное указание типа. Если код позволяет обработку любого object и нормально компилиться и при этом вы все равно хотите запретить какой-то тип на уровне локальной переменной это опять же выглядит неверным инструментом решения вашей проблемы. Не стоит дырки плохого дизайна пытаться решать подобного рода валидацией. Лучше не будет.

Например, не заглядывая в конкретные места использования разработчик сделал метод Get асинхронным. С каким кодом ему было бы проще столкнуться:

— var model = _repository.Get(modelId);
+ var model = _repository.GetAsync(modelId);
return View(model); // Runtime error.

или

— Entity model = _repository.Get(modelId);
+ Entity model = _repository.GetAsync(modelId); // Compiler error.
return View(model);

Мой исходный коммент именно об этом. Нужно было сразу пример привести.

Разработчику было бы не плохо запустить свое творение один раз.

А так же написать Unit, Integration и Acceptance тесты. Тут вопрос compile time vs. runtime error. Я думаю всем понятно, что предпочительнее, особенно для леммингов. Но вопрос то не в этом. Ребята, нравится стрелять себе в ногу — пожалуйста. Вон в соседнем топике писали, что дебаг это увлекательное занятие. Я думаю у меня получилось донести свою мысль с конкретными примерами. Дальше пусть каждый сам принимает решения.

Ваш пример притянут за уши. Метод View (кстати говоря что это за название такое??? ) по хорошему не должен принимать Task<entitity>

Это код в контроллере ASP.NET MVC. View принимает object. Очень даже реальный пример. Или, как Дмитрий написал ниже, нужно модель сериализовать в JSON и вернуть клиенту. С var вы сериализуете таску. Тоже были прецеденты.

Это проблема ASP.NET, а не неявной типизации.

Ок, спасибо за объяснение. Это не моя специализация.
Мое мнение — опытный разработчик(как вы например) знает об этом моменте и просто не напишет кода с такой ошибкой. То же самое что писать константу слева от оператора сравнения. Теоретически смысл есть, а практически проблема которую решает подобный подход проблемой не является. А читабельность кода снижается заметно

Это не моя специализация.
если бы это была твоя специализация (хотя прогер любой технологии тут поймет о чем речь), ты бы не писал выше эту хрень:
Это не показывает ничего, окромя того топик стартер склочная женщина.
А писал бы по теме)

вы еще скажите им что юнит тесты надо писать

ребята, творение запущено и тестами покрыто, не напрягайтесь)

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

Мы эту конвенцию не форсим, просто есть случаи когда лучше с ней, поэтому я ее придерживаюсь. Да и code cleanup в R# все эти вещи приводит к общему виду наура.

У нас проекте когда я пришел все было синхронным/блокирующим и очень много мест перевели на асинхронный подход. Это длительный постепенный процесс и начался больше года назад. И в процессе несколько раз наступали на похожие грабли. С методами возвращиющими значение проще, а с void вообще засада — запустил таску и пошел дальше выполняться метод :). Так что ревью и тесты — наше все.

Сейчас да, по-умолчанию для нового кода I/O асинхронное.

В node.js вон вообще обратная конвенция — суффикс Sync маркирует синхронную операцию, а асинхронная немаркирована.

С учётом того, насколько всё катится к тотальной асинхронности, это выглядит более разумным.

Тотальная асинхронность до добра не доведет. Нужна тотальная синхронность, как в Go.

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

Венгерская нотация (или как ее называют) от лукавого, ясен пень что лучше без нее

Венгерская нотация это вообще жесть, не думаю, что Async оттуда. Просто MS в какое-то время начал реазовывать асинхронные методы в допонение к синхронным и решили различать их таким образом, например, client.Send и client.SendAsync. Send оставили синхронным для обратной совместимости.

В .Net в текущем состоянии асинхронный код(любые таски, а не только неблокирующий i/o) практически всегда будет иметь отличительную сигнатуру. если ваши асинхронные методы сервисов настолько схожи с синхронными, что вам Async выдается избыточным(не предусматривают поддержки cancelation tokens, не учитывают специфику syncronization context окружения и фреймворка, который их используют и на котором вызываються) вы скорее всего всего используете асинхронный код не лучшим образом.

CancellationToken передается по необходимости, await конфигурится в false, если явно не нужен контекст синхнонизации. С асинхронным кодом я достаточно детально разобрался и научился его готовить. Тем не менне это не помешало мне отказаться от Async. В нашем приложении практически везде есть асинхронные операции (обращение к бд, сервисам, файлам). Т.е. код от точки входа и дальше асинхронный.

Називаючи так метод, ви розкриваєте деталі імплементації, що як мінмум негарно, ну а я б сказав просто убого.

Ваше мнение очень нам важно.

Як і мені ваше. Почитайте Роберта Мартіна, о «великий сеньйоре».

Ті деталі імплементації, які критично важливі для сутності дії і не можуть бути уніфіковані деяким шаром абстракції, навпаки, повинні бути явно названі.
Не посилайтеся на Мартіна, якщо його не зрозуміли.

Чому це не вирішується шаром абстракції? Наприклад, є у нас два сервіси AsyncService та SyncService. Робимо, щоб обидва імплементили інтерфейс Service — маємо шар абстракції. Клієнт працює з Service, а не з конкретною імпелментацією. Плюс це дає можливість скейлення, коли додасться ще одна реалізація Service. В чому проблема?
І з чого ви взяли, що ви розумієте Мартіна? Тут завжди буде поле для дискусій, хтось може з ним не погоджуватись або розуміти по-іншому.
П.С. Я не зав’язуюсь на С# у вищенаведеному прикладі взагалі.

Вы хоть понимаете чем синхронный код отличаеться от асинхронного и что аргументы\возвращаемые значения у абстракций таких будут разными ?
Если AsyncService засинхронизировал себя внутри и вернет тот же значение, что и SyncService, зачем писать AsyncService?!

В принципе если в какой-то момент нам будут нужны результаты, в смысле вполне реальные, а не только обещание что они там когда-то появятся, то ждать завершения async вызова таки придется. То есть допустим если мы создаем объект: в случае синхронного варианта мы ждем данные и сразу их инициализируем в объекте, а в асинхронном случае: ждем данные уже когда к ним реально обращаются. Это может иметь смысл, хотя городить для этого слой абстракций — не факт что всегда хорошая идея.

Ничего не понял. Это как-то относится к тому что асинхронная и синхронная версия метода могут иметь общий интерфейс? Давайте пример кода.

А причем тут метод? Выше про синхронную и асинхронную имплементации для интерфейса.

Ок, хотите методы, ради бога:

function do(args, sync, callback){
   if(sync){
       var results = doSync(args);
       callback(results);
   }else{
      doAsync(args, callback);
   }
}
* есс-но правильней будет doSync(args, callback), тут для наглядности.

++
Ну или если хочется

class SyncSmth implements Smth{
  function do (callback){
  }
}

class AsyncSmth implements Smth {
   function do (callback){
   }
}

interface Smth {
  function do (callback)
}

++
или

class Smth {
   function getVal1()
   ...
   function initiFromResult(result)
}

class SyncSmth (Smth){
   function new {
      result = syncCall()
      initFromResult(result)
   }
}

class AsyncSmth (Smth){
      function new {
          asyncCall(initiFromResult)
      }
      function getVal1(){
           synchronize()
           super()
       }
      ...
}
Не буду переводить на C#, не помню его. Идея понятна я думаю.

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

Вы каждый раз когда можете распаралелить задачи ввнутри вызываемого метода во время оптимизации кода создаете рядом новую реализацию и даете ей название Async? Мне кажется тут бы вам больше подошли имена классов нечто вроде TwoTimesFasterSmth.cpp, ThreeTimesFasterSmth.cpp etc.

По факту, код негоден. Вам пришлось нарушить много правил проектирования и сдравого смысла, что бы доказать что асинхронный код можно выдать за синхронный? Пойдете писать такой код в продакшин или напишите отдельно две сигнатуры как все нормальные люди?
Task<int> DoAsync();
int Do();

ок, ок. На самом деле это вопрос того какого эффекта вы хотите достичь и как клиент API вашего класса должен, по вашему мнению, думать о его использовании. Например:

Task<int> DoAsync();
int Do();

хороший вариант на самом деле, я не спорю, если мы хотим предоставить клиенту возможность выбора. Собственно это то что делает MS, я так понимаю. General purpose API, они не знают как вы его захотите использовать, поэтому вот вам оба варианта, удачи так сказать.

Но, есть и другая сторона вопроса. Ваше API может выдавать клиенту кошечек и прямоугольники. И клиенту пофигу как они получены (асинхронные кошечки никак не должны отличаться от синхронных). Поэтому в какой-то момент мы абстрагируем процесс получения async/sync и выдаем просто нормального здорового кота.

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

Да, к таким построениям стоит относится с осторожностью, вполне возможно что можно/стоит сделать проще. С другой стороны: ничего особо странного в таких построениях нет (при всей корявости, например третьего варианта).

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

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

та ну, тогда и get*/set* надо убрать, тогда будет просто метод name, а внутри if: если передали параметр, то работает как сеттер, если не передали — как геттер?

Ну в принципі так, геттери і сеттери це розкриття реалізації, простіше тоді зробити всі поля публічними, щоб не засмічувати код. Те, що такі об’єкти це швидше структури даних, ніж об’єкти — це вже інше питання, але випадку sync/async це не стосується.

простіше тоді зробити всі поля публічними, щоб не засмічувати код.

Просто поля? Автори всяких WinForms не погодяться...

если передали параметр, то работает как сеттер, если не передали — как геттер?
JQuery-куны плюсуют

Это они у Java спёрли, если не раньше

Та это вроде не только джквери

Так у них же сигнатуры открыто разные, не пойдёт за сокрытие. И вообще, в рамках темы это подходы б-гомерзкой Java, в отличие от истинно православнымх пропертей в C#, где таки сокрыто под видом переменной, и если у вас скорость падает в 20 раз за счёт того, что наивное += сначала долго читает, а потом долго пишет — то иншалла.

А что у вас там в дотнете за фишки с этими тасками? Я бы ожидал что и так не скомпилируется, так как в классе Task нет методов и полей класса Entity.

Если к полям обращаться, то да. Но ошибка будет в месте обращения. И может не быть очевидной причина. Но не всегда идет обращение к полям, как в моем примере выше dou.ua/...aign=reply-comment#916618.

Не жизненно имхо. Во-первых, упадут юнит тесты (ведь они у вас там есть, да?). Во-вторых, таких, кто меняет свой метод и плевать хотел на все референсы, нужно как минимум понижать в должности. Да. явное указание типа сбережет некоторое время в данном случае, но такой разработчик потенциально — источник более серьезных проблем в будущем. Он не думает о приложении в целом.
Ну, а в-третьих, зачем мне морочить голову с типами, если я получил что-то и передал это в пару других методов? Пусть компилятор разбирается. Его время бесплатно.
Я согласен с тем, что «отцы основатели» задумывали var для dto из linq query, но практики нашли ему более широкое применение )

я получил что-то и передал это в пару других методов
Явный тип всего лишь поможет проще понять что именно получено и передано без лишних телодвижений.

Но я так до конца и не понял, причина дискуссии то, что явные типы визуально захламляют код или в том, что лень указать явный тип? Если первое — возможно имена типов подобраны не лучшим образом? Если второе — есть же инструменты типа R#, которые умеют это делать автоматически. Никого, ведь, не смущет, надеюсь, что для параметров и полей, и возвращиемых значаний нужно указывать тип?

Собственно, нет никакой железобетонной причины следовать одному стилю или другому. Мне нравится просто набрать var и затем — метод. Если есть тулза, которая после этого может поменять var на явный тип — тоже не плохо. Но только в предрелизном варианте. Пока идет разработка, с varом удобнее. Тот же решарпер по-дефолту хочет var.
Клиентам, проводящим код ревью, var не нравится. Но они не тратили несколько часов на метод, который у них перед глазами.
Поэтому, я в принципе не против, чтобы в билд тасках была одна: «поменять на явные типы для того типа из центрального Лондона»

Клиентам, проводящим код ревью, var не нравится.
Не знал что столь сложные случаи бывают

Автовыведение пришло из функциональщины и было до появления linq.

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

Оу, как колко, touche! Хотя, погоди, может, и ссылочку предоставишь на ворнинг in question? А заодно попробуй подумать появится ли тот самый ворнинг если метод не помечен async. Ну, и на всякий случай для совсем ленивых: dou.ua/...orums/topic/17285/#916612.

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

Пример 1:
Компактый код, который сложно читается:

_entityRepository.Update(MapToPoco(holidayVM));

Легко читаемый код (добавили имена переменных):

var entity= MapToPoco(holidayVM);
_entityRepository.Update(entity);

Перегруженный код:

Entity entity= MapToPoco(holidayVM);
_entityRepository.Update(entity);

Пример 2:
Компактый код, который сложно читается:

_entityService.UpdateEntityWithDates(entity.Date, Convert.ToDateTime(entityVM.Date));

Легко читаемый код (добавили имена переменных):

var oldDate = entity.Date;
var newDate= Convert.ToDateTime(entityVM.Date);
_entityService.UpdateEntityWithDates(oldDate, newDate);

Перегруженный код:

DateTime oldDate = entity.Date;
DateTime newDate= Convert.ToDateTime(entityVM.Date);
_entityService.UpdateEntityWithDates(oldDate, newDate);

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

Я рекомендую придерживаться соглашений MS Coding Conventions в этом вопросе. То, что прописано в разделе «Implicitly Typed Local Variables» по Вашей ссылке появилось там не просто от нечего делать.
1) В случаях, когда тип явно не следует из правой части использование «var» создает ряд проблем и не рекомендуется. Это не только заметно ухудшает читабельность кода, особенно чужого, но и может привести к неожиданным багам. К примеру, у меня в практике был случай, когда в гриде клиент попросил добавить новый столбец и присуммировать его значения в Total. Изначально код имет такой вид:


var totalCellValue = GetBoY() + GetContributions() - GetDistributions();
После добавления еще одной колонки код стал выглядеть так:

var totalCellValue = GetBoY() + GetContributions() - GetDistributions() + GetCorrections();
Код скомпилировался без проблем, а все значения возвращаемые GetCorrections() были равны «0». Вот только при проверки выяснилось, что до внесения изменений все значения в колонке «Total» были отличны от нуля, а после добавления GetCorrections(), все значения в колонке «Total» стали равны только «0».
Причина бага была в том, что первые 3 метода возвращали «decimal», а вот новая колонка возвращала «decimal?». И хотя null там никогда не возвращался, но тип totalCellValue также в результате незаметно поменялся с decimal на decimal? и при скармливании этого значения в грид последнему сносило крышу и он просто рисовал «0» вместо реальных Value.
2) В случаях, когда тип явно следует из правой части или в циклах for/foreach, использование var вполне нормально — это несколько ускоряем написание кода и не сказывается негативно на его читабельности. Использовать или нет явную типизацию в данных случаях — это вопрос соглашений внутри команды — тут гораздо хуже, если разные люди в команде будут использовать разные подходы. Т.е. тут как договорились в команде — так и делайте, главное договориться и делать одинаково.

Если отталкиваться строго от соглашений Микрософта, то Ваш пример должен выглядеть так:


public void Update(EntityViewModel entityVM)
{
// Из правой части тип не ясен, поэтому var не рекомендуется.
// Это единственное место, где я не соглавен в Вашей тимой.
Entity entity = MapToPoco(entityVM);
_entityRepository.Update(entity);

// Допустимы оба варианта. Здесь тип очевиден из правой части.
// Явное объявление потребуется, если в entity.Date не DateTime
var oldDate = entity.Date;

// Допустимы оба варианта. Явное объявление типа не требуется.
// ToDateTime не оставляет сомнений в том, какой тип вернется
var newDate= Convert.ToDateTime(holidayVM.Date);

_entityService.UpdateEntityWithDates(oldDate, newDate);
}

спасибо за конструктивный ответ

про LINQ еще забыли
там без имплисит декларации никуда и вообще само var было прикручено спецом для LINQ
ну и послание к ТС чтоб два раза не писать:
в МСДН пишут:


Local variables can be given an inferred “type” of var instead of an explicit type. The var keyword instructs the compiler to infer the type of the variable from the expression on the right side of the initialization statement. The inferred type may be a built-in type, an anonymous type, a user-defined type, or a type defined in the .NET Framework class library. For more information about how to initialize arrays with var, see Implicitly Typed Arrays (C# Programming Guide).
msdn.microsoft.com/...-us/library/bb384061.aspx
то есть юзайте var, но помните что результат иногда может быть неожиданным ))

вопрос был задан в применении var в крудах методов сервиса. с linq и бобру понятно что var.. перечислять все случаи ЗА var в других случаях..? топик о четком моменте его использования — стиле бизнес логики

логики, использующей позднее связывание здесь нет. Потому dynamic упоминать смысла нет

имеется ввиду что у шарпа в обеих случаях явная типизация. и вопрос только в стиле написания кода.

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

Если что, то Тима прав/а в контексте этого примера.

вопрос в явной либо неявной типизации переменных как стиле CRUD методов. Разницу понимаю, вопрос в другом

вопрос в явной либо неявной типизации переменных как стиле CRUD методов. логики, использующей позднее связывание здесь нет, dynamic, dear Andrii Zhuk, тут не при чем.
Тогда не надо про строгую типизацию. Слабо типизирован в Шарпе только dynamic.
прописывайте тип явно, где согласно контексту неочевидно, каков тип
Стандарт. Который гораздо лаконичнее, чем Борщ борщ = new Борщ().

имелась ввиду явная типизация

Да я то понимаю, просто в теме написанно строгая. Что до сабжа — вар везде, где тип очевиден.

в Борщ борщ = new Борщ() виден тип справа, это объявление. а когда получаешь сущность из некоторого метода свыше и не виден тип в левой части. и таких строк внутри метода несколько. То слева явно указать тип ой как намного удобнее.

Entity entity = MapToPoco(entityVM);
_entityRepository.Update(entity);

А мне тут нужно смотреть на тип entity? Нет, ты его не используешь. Оно сразу идёт в расход. С натяжкой можно указать тип, но не обязательно.

DateTime oldDate = entity.Date;
DateTime newDate= Convert.ToDateTime(holidayVM.Date);

В обоих случая тип — очевиден. Так нафига попу гармонь.

А мне тут нужно смотреть на тип entity? Нет, ты его не используешь. Оно сразу идёт в расход. С натяжкой можно указать тип, но не обязательно.
А потом через пару недель тыкать мышкой и смотреть:"А шо то МапТуПоко возвращет?"
Имхо, var надо юзать там, где его замена на «явную» альтернативу будет стоить дорого(анонимные типы, LINQ).

И пойдёте смотреть это в какой то левый метод? В крайнем случае — можно вышкой навести. Но тут случай ещё двоякий, можно так и так, чего не скажешь про две переменных ниже.

а если ревьювим код репы, в браузере,на bitbucket предположим? там мышкой не наведешь..код то читается не только в ide

Страдание фигнёй. Ревьювить код без инструментария — дурное дело.

А историю коммитов просмотреть, например?

Ревью кода и история коммита — разные задачи.

Разные. Я смотрю историю в SourceTree, например. И там явная типизация (если тип переменной неочевиден) намного упрощает понимание.

Я смотрю в Студии и мне — всё равно.

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

К мордам версионирования, типа Gerrit, штатно привинчиваются CI, типа Jenkins, а там можно и тесты, и анализаторы, и я всё это использовал готовое.
Если «обычно» это практика крупных курятников, то кто им доктор.

С таким успехом вам это мало чем поможет, ведь вам все равно тогда надо лезть смотреть что это за класс/ интерфейс, ибо не упомнишь.

Надо не сходить с ума, а думать.

Задайте себе вопрос, получите ли вы реальный бенефит, а если и получите то чем заплатите?

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

Куча вещей которыми ты пользуешься, скрыто в реализации, в том числе и типы объектов.
var в этом случае это просто манна небесная, т.к. по сути тебе часто вообще нифига не надо знать о типе, кроме того, что весь тот Борщ IEnumerable.
Да и сам стиль, как отмечалось выше, на много приятней и читабельней.

Ну тогда скажи, что эта переменная IEnumerable типа, чтобы тому, кто потом будет читать, было понятно, что ты от нее ожидаешь.

Підписатись на коментарі