Почему var может быть плохим?

На днях я болтал с моими коллегами и услышал, что их стандарт кодирования явно запрещает им использовать ключевое слово var в C #. Они понятия не имели, почему это так, и я всегда считал неявное объявление невероятно полезным при кодировании. У меня никогда не было проблем с выяснением, какого типа была переменная (вы только наводите курсор на переменную в VS, и вы получите тип таким образом).

Кто-нибудь знает, почему было бы плохой идеей использовать ключевое слово var в C #?


person Spoike    schedule 13.02.2009    source источник
comment
Интересно, было ли это решение решением большинства / консенсуса команды разработчиков или оно было единолично объявлено каким-то менеджером.   -  person Tamas Czinege    schedule 13.02.2009
comment
@DrJokepu: Я не спрашивал, так как мы перешли на LINQ. Но не удивлюсь, если об этом объявит своего рода менеджер.   -  person Spoike    schedule 13.02.2009
comment
Вам должно повезти, потому что вам не нужно работать со стандартом кода, как здесь: thedailywtf.com/Articles/Astigmatism.aspx   -  person Codism    schedule 20.05.2010
comment
Вы (обычно) не получаете тип при наведении курсора на переменную, если кто-то (весьма услужливо) украсил тип атрибутом DebuggerDisplay.   -  person fractor    schedule 12.12.2014
comment
var активно ПОДДЕРЖИВАЕТСЯ в стандартах кодирования C #, так что это было бы бесполезно идти против этого правила. var здесь не зря. И совершенно очевидно, что это за шрифт, даже не наводя на него курсор. var x = new Customer(); Это объект "Клиент". Простой :)   -  person ManoDestra    schedule 02.08.2016
comment
@ManoDestra , поэтому было бы бесполезно идти против этого правила Нет, не будет. Когда предпочтение двусмысленности ясности в программировании когда-либо является хорошим делом? Это довольно слабый пример, который не охватывает всех случаев использования. А что насчет var foo = bar.Coords;? Что такое Coords? Массив поплавков? Интс? Совершенно другой объект? Если вам приходится полагаться на свою IDE для обеспечения такой ясности, значит, что-то серьезно не так с вашими стандартами кодирования. И, Боже, помоги любым новым программистам, которых ты найдешь.   -  person b1nary.atr0phy    schedule 08.08.2016
comment
Никакой зависимости от IDE. Это прекрасно видно из исходного кода.   -  person ManoDestra    schedule 08.08.2016


Ответы (17)


Авторы Руководства по проектированию .Net Framework (потрясающе book), вышедшие в ноябре 2008 г., рекомендуют рассмотреть возможность использования var, когда Тип очевиден и однозначен.

С другой стороны, если использование var приведет к неоднозначности при чтении кода, как указал Антон Гоголев, то лучше не использовать его.

в книге (приложение А) они фактически приводят такой пример:

var names = new List<string>(); // good usage of var

string source = GetSource();
var tokens = source.Split(' '); // ok; most developers know String.Split

var id = GetId(); // Probably not good; it's not clear what the type of id is

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

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

person Renaud Bompuis    schedule 13.02.2009
comment
Основная мысль, которую, как мне кажется, все упускают в этой войне, - это навести указатель мыши на переменную. Я не вижу, что здесь возвращает GetId (), но как часто вы читаете сложный код, которого нет в вашей IDE? Инструменты - важная часть языка. - person John Oxley; 08.09.2009
comment
зачем наводить курсор на каждую переменную, если можно просто прочитать ее без лишних усилий? wtf - person alchemical; 06.02.2010
comment
@alchemical: а что возвращает GetId (), не просматривая интерфейс и не наводя курсор мыши на элемент? - person RvdK; 20.05.2010
comment
@PoweRoy: Я думаю, что это была его точка зрения - это всегда очевидно, когда вводится явная информация .. long id = GetId (); - person DefenestrationDay; 15.06.2011
comment
Основываясь на ваших примерах, использование var с операторами LINQ - плохая идея, потому что неясно, какой тип идентификатора. - person Cole Johnson; 07.09.2014
comment
@ColeJohnson LINQ не существовало на момент выпуска книги. В настоящее время мы используем var для сложных типов (LINQ, lambda), но практическое правило остается верным, поскольку эти типы определены там, где они объявлены, и обычно нет реальной двусмысленности. Если есть, то объявление, вероятно, должно быть явным. - person Renaud Bompuis; 11.09.2014
comment
@DefenestrationDay: если вы не пишете это впервые. Вы можете всегда набрать var id = GetId();. Если я предполагаю, что GetId возвращает int, и набираю его int id = GetId();, теперь у меня ошибка компиляции, если GetId возвращает GUID. Мне все еще нужно это найти. - person Bryan Boettcher; 25.09.2014
comment
Есть много случаев, когда вам действительно не нужно заботиться о том, какой идентификатор типа. Это то, что вы получаете от одного вызова метода и часто переходите к другому вызову, практически не взаимодействуя с ним. Его тип так же важен для моего кода, как MacGuffin в случайном фильме Хичкока. - person Vincent Vancalbergh; 10.11.2014
comment
Хотя это имеет смысл и делает типы более очевидными для человека, читающего код ... теперь у вас есть несогласованные объявления. Некоторые неявные, а некоторые явные, чтобы вы могли использовать вары, когда это явно проще. Теперь мы должны смотреть по обе стороны уравнения в зависимости от очевидности кода. Если вы используете явные объявления для упрощения чтения, упростите его еще больше, сохранив согласованность. Кроме того, тот, кто считает приемлемым для людей читать, наводя курсор мыши на неявные слова, должен подумать еще раз, это только замедлит нас и ухудшит читаемость. - person Andy Clark; 10.02.2015
comment
Единственный раз, когда я использую var, - это анонимные типы, и пока я объявляю переменную, я не уверен, что вернется. Как только я узнаю, какой тип возвращается, я заменяю var на явный тип. - person Andy Clark; 10.02.2015
comment
Я знаю String.Split (), но сразу не мог вспомнить, возвращает ли он массив строк или список строк. - person Jon Schneider; 19.12.2016
comment
var о неоднозначных объявлениях также является большой проблемой при проверке запросов на вытягивание в веб-интерфейсах, таких как bitbucket или github. Вы не можете навести объявление, чтобы получить тип. Лично у меня очень строгая политика в отношении этого: используйте var только при использовании new на другой стороне операции (включая анонимные типы) или при использовании явного приведения (хотя я обычно не использую var и здесь). - person Maxime Rossini; 23.03.2017
comment
@alchemical В подавляющем большинстве случаев тип возвращаемого объекта не нужно знать сразу. Так зачем вам наводить курсор на переменные, чтобы увидеть их тип? Потому что это значительно упрощает чтение кода (за счет отсутствия избыточной информации, загромождающей ваш код) в подавляющем большинстве случаев. Вот почему. - person B T; 24.08.2018

var q = GetQValue();

это действительно плохо. Тем не мение,

var persistenceManager = ServiceLocator.Resolve<IPersistenceManager>();

меня совершенно устраивает.

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

В качестве примечания: мне интересно, как они справляются с анонимными типами, когда им запрещено использовать ключевое слово var. Или вообще ими не пользуются?

person Anton Gogolev    schedule 13.02.2009
comment
Моя текущая работа поддерживает кодовую базу, созданную на основе .NET 1.1. Хотя сейчас он модифицирован для работы под 3.5, большая часть кода использует принципалов 1.1. Я полагаю, что многие разработчики находятся в одной лодке и не используют анонимные типы по привычке. - person Greg; 20.05.2010
comment
@Greg Мы здесь не говорим об анонимных типах. - person Anton Gogolev; 20.05.2010
comment
Я обнаружил, что часто после рефакторинга хорошо названная переменная указывает на старый тип и может вводить в заблуждение. Если тип явный, то проблему такого рода легко обнаружить. - person fractor; 12.12.2014
comment
var q = GetQValue(); это не плохо. На самом деле это рекомендация стандарта программирования C #. Тип q будет немедленно выведен из типа возвращаемого значения этого метода. Простой :) - person ManoDestra; 02.08.2016

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

Бывают случаи, когда неясно, что вы что-то сломали, изменив это - в основном, когда инициализированный тип и (исходный) тип переменной не совпадают, потому что:

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

В этих случаях у вас могут возникнуть проблемы с любым разрешением типа, например:

  • методы, которые имеют разные перегрузки для двух конкурирующих типов
  • методы расширения, которые определены по-разному для двух конкурирующих типов
  • члены, которые были повторно объявлены (скрыты) на одном из типов
  • вывод универсального типа будет работать по-другому
  • разрешение оператора будет работать иначе

В таких случаях вы меняете смысл кода и выполняете что-то другое. Тогда это плохо.

Примеры:

Неявное преобразование:

static void Main() {
    long x = 17;
    Foo(x);
    var y = 17;
    Foo(y); // boom
}
static void Foo(long value)
{ Console.WriteLine(value); }
static void Foo(int value) {
throw new NotImplementedException(); }

Скрытие метода:

static void Main() {
    Foo x = new Bar();
    x.Go();
    var y = new Bar();
    y.Go(); // boom
}
class Foo {
    public void Go() { Console.WriteLine("Hi"); }
}
class Bar : Foo {
    public new void Go() { throw new NotImplementedException(); }
}

и т.д

person Marc Gravell    schedule 13.02.2009
comment
хорошие примеры, никогда не думал об этом - person RvdK; 20.05.2010
comment
Другой пример: Смена автомобиля myCar = BuickFactory.MakeCar; в var myCar = BuickFactory.MakeCar; не может изменить код, если BuickFactory.MakeCar возвращает Car, но если более поздняя версия BuickFactory затеняет MakeCar для возврата Buick, тип переменной myCar изменится. Если запущенный код пытается присвоить myCar какой-либо другой тип автомобиля, он завершится ошибкой. - person supercat; 28.01.2011

Конечно, это ошибка. Это потому, что некоторые люди не понимают, что это на самом деле строго типизировано, и совсем не похоже на var в VB.

Не все корпоративные стандарты кодирования имеют смысл. Однажды я работал в компании, которая хотела добавить к имени компании префикс всех имен классов. Когда компания сменила название, произошла масштабная переделка.

person NeedHack    schedule 13.02.2009
comment
Вы имеете в виду вариант, а не var :) - person dreamlax; 13.02.2009
comment
Вам должно быть стыдно признаться, что вы помните! ;-) - person NeedHack; 13.02.2009
comment
Ой! @ Приставка к классам имени компании. - person Spoike; 13.02.2009
comment
LOL, есть эта проблема прямо сейчас. это так хорошо, когда у вас есть рефакторинг. - person Avram; 13.02.2009
comment
Я проделал то же самое, переименовав все функции, когда компания решила использовать другой префикс. - person MrZebra; 13.02.2009
comment
Мы написали целую структуру и инструменты для переименования вещей в коде, VSS, именах файлов Windows и Exchange за один раз (в 2001 году на VB6). Да, у них были сумасшедшие стандарты именования, и они меняли их почти ежедневно. - person kpollock; 13.02.2009
comment
@kpollock Они, должно быть, действительно сумасшедшие! - person chakrit; 27.10.2009
comment
Извините, но когда компания хочет, чтобы я поставил перед всеми переменными название их компании, я, скорее всего, назову их do # ches ... - person Tarik; 24.12.2009
comment
@ Я мог бы понять, произнесли ли они суффикс, но префикс полностью испортил читаемость кода ... - person Tarik; 24.12.2009
comment
Почему никто не упомянул NeXTSTEP :) - person steve cook; 25.02.2014

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

Во-вторых, это правило, вероятно, оправдано, потому что код больше раз читается, чем пишется. var ускоряет запись, но может немного замедлить чтение. Очевидно, что это не правило поведения кода, такое как «Всегда инициализировать переменные», потому что две альтернативы (запись var и запись типа) имеют точно такое же поведение. Так что это не критическое правило. Я бы не стал запрещать var, я бы просто использовал "Предпочитаю ..."

person Daniel Daranas    schedule 13.02.2009

Несколько месяцев назад я написал в блоге статью на эту тему. Что касается меня, я использую его везде, где это возможно, и специально разрабатываю свои API для вывода типов. Основные причины, по которым я использую вывод типов:

  1. Это не снижает безопасность типов
  2. Фактически это повысит безопасность типов в вашем коде, предупредив вас о неявном приведении типов. Лучший пример в инструкции foreach
  3. Поддерживает принципы DRY в C #. Это специально для случая объявления, зачем говорить имя дважды?
  4. В некоторых случаях это совершенно необходимо. Примеры анонимных типов
  5. Меньше набора текста без потери функциональности.

http://blogs.msdn.com/jaredpar/archive/2008/09/09/when-to-use-type-inference.aspx.

person JaredPar    schedule 13.02.2009

var - это последняя новость о том, "как поставить брекеты" / венгерские обозначения / дебаты о верблюжьей оболочке. Нет правильного ответа, но есть люди, сидящие на крайностях.

Вашему другу просто не повезло, что они работают под одним из экстремистов.

person Garry Shutler    schedule 13.02.2009

Полный запрет означает запрет на использование анонимных типов (которые становятся невероятно полезными по мере того, как вы больше используете LINQ).

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

person ShuggyCoUk    schedule 13.02.2009

Что такое var на простом английском

Я собираюсь показать вам, что использование AND без использования var означает четкое общение.

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

Более того, вы увидите, насколько ясна 'var', во многом зависит от того, как вы называете все остальное в своем коде.

Возьмем этот пример:

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

Кто пошел другой дорогой? Джейк или Билл? В этом случае «Джейк» и «Билл» похожи на название типа. А «Он» и «его» похожи на ключевое слово var. В этом случае было бы полезно уточнить детали. Следующий пример намного яснее.

Джейк поздоровался с Биллом. Джейку не понравился Билл, поэтому он повернулся и пошел другой дорогой.

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

Билл любит книги, поэтому Билл пошел в библиотеку, и Билл достал книгу, которая ему всегда нравилась.

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

Биллу нравятся книги, поэтому он пошел в библиотеку и достал книгу, которая ему всегда нравилась.

Эти аналогии охватывают суть, но не раскрывают всей истории. Видите ли, в этих примерах был только один способ обозначить человека. Либо своим именем, например Билл, либо более общим образом, например, «он» и «он». Но мы работаем только с одним словом.

В случае с кодом у вас есть два «слова»: тип и имя переменной.

Person p = GetPerson();

Теперь возникает вопрос: достаточно ли информации, чтобы вы могли легко определить, что такое p? Вы все еще знаете, какие люди в этом сценарии:

var p = GetPerson();

Как насчет этого:

var p = Get();

Как насчет этого:

var person = Get();

Или вот этот:

var t = GetPerson();

Или вот этот:

var u = Person.Get();

Будет ли ключевое слово var работать в данном сценарии, во многом зависит от контекста кода, например от имен переменных, классов и методов, а также от сложности кода.

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

Тем не менее, иногда я делаю исключения, такова природа чего-либо сложного, а программное обеспечение - ничто иное, как сложное.

person Luis Perez    schedule 17.06.2016
comment
Имена, как и комментарии, могут устареть. Типы не могут устареть, они проверяются компилятором. - person john16384; 21.10.2017
comment
@ john16384, если под устаревшим вы имеете в виду, что код может измениться, так что комментарий и имена больше не будут точно описывать то, что делает код, это правда. Но это также применимо к именам классов: вы можете изменить имя класса, чтобы имя больше не имело смысла, точно так же, как вы можете изменить имя переменной. И вы можете изменить код в классе / типе до такой степени, что имя класса больше не точно описывает то, что он делает. Я считаю, что это совсем другой вопрос. В этом случае правильнее всего переименовать объекты независимо от использования слова var. - person Luis Perez; 12.01.2020

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

person Andrew Hare    schedule 13.02.2009
comment
Спросите себя, почему они используют анонимные типы. Имена предоставляют информацию программистам - они улучшают понимание. - person ; 07.06.2017

Вы можете считать мнение Microsoft актуальным, поскольку C # - их язык:

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

См. MSDN - неявно типизированные локальные переменные (программирование на C # Руководство), последний абзац.


Вы также должны знать, что var удаляет проверку типа данных во время компиляции при начальном назначении.

var x = "mistake";     // error not found by compiler
int x = "mistake";     // error found

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

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

person Community    schedule 09.09.2014

Это действительно проблема с читабельностью вашего кода.

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

Однако C # 3.0 с радостью позволит вам использовать var где угодно, кроме LINQ и анонимных типов, например:

var myint = 0;
var mystring = "";

совершенно корректно, а myint и mystring будут строго типизированы по предполагаемым значениям, используемым для их инициализации. (таким образом, myint - это System.Int32, а mystring - это System.String). Конечно, довольно очевидно, если посмотреть на значения, используемые для инициализации переменных, к каким типам они будут неявно типизированы, однако я думаю, что для читабельности кода было бы даже лучше, если бы приведенное выше было написано как:

int myint = 0;
string mystring = "";

так как вы можете сразу увидеть, к какому типу относятся эти переменные.

Рассмотрим этот несколько сбивающий с толку сценарий:

var aaa = 0;
double bbb = 0;

Совершенно правильный код (если немного нетрадиционный), но в приведенном выше примере я знаю, что bbb является двойным, несмотря на то, что инициализирующее значение выглядит как int, но aaa определенно будет не двойным, а скорее int.

person CraigTP    schedule 13.02.2009
comment
Честно говоря, я нахожу пример инициализации объекта одинаково читаемым в обоих направлениях. Для меня объявление типа, когда это уже видно на виду, является избыточным визуальным шумом. - person Spoike; 13.02.2009
comment
Поскольку использование var не снижает производительности, это довольно субъективная вещь. Однако это может стать более запутанным, если вы инициализируете переменную с возвращаемым значением другой функции. (т.е. var a = MyFunc ();), поскольку теперь вам нужно знать, что возвращает MyFunc, чтобы узнать, что такое a. - person CraigTP; 13.02.2009

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

Это почти похоже на типобезопасную, проверенную компилятором утиную типизацию, которая невероятно полезна при рефакторинге. Например, если у меня есть метод, который возвращает список, и я реорганизовал его, чтобы вернуть IEnumerable, тогда все вызовы этого метода, которые использовали ключевое слово var и используют только методы IEnumerable, будут в порядке. Если я явно указал, например, List, то мне нужно везде изменить его на IEnumerable.

Очевидно, что если какой-либо из вызывающих объектов с неявной типизацией требует методов List, я получу ошибки компиляции при сборке, но в этом случае мне, вероятно, все равно не следовало менять возвращаемый тип.

person Mark Rendle    schedule 13.02.2009

Эрик Липперт, суммы это хорошо:

  • При необходимости используйте var; когда вы используете анонимные типы.
  • Используйте var, когда тип объявления очевиден из инициализатора, особенно если это создание объекта. Это устраняет избыточность.
  • Рассмотрите возможность использования var, если код подчеркивает семантическую «бизнес-цель» переменной и преуменьшает «механические» детали ее хранения.
  • Используйте явные типы, если это необходимо для правильного понимания и поддержки кода.
  • Используйте описательные имена переменных независимо от того, используете ли вы «var». Имена переменных должны отражать семантику переменной, а не детали ее хранения; «decimalRate» - это плохо; "InterestRate" - это хорошо.

Мое собственное мнение: мне труднее читать и немного бессмысленно с такими типами, как int, string, bool или даже User. В конце концов, речь идет о удобочитаемости (кроме случаев, когда его используют с LINQ), поэтому, когда вары разбросаны, может быть труднее прочитать и победить цель ключевого слова, для которой разработчики языка предусмотрели его.

person Chris S    schedule 22.01.2013

Из Департамента избыточности деклараций (из Джеффа Coding Horror):

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

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

person doekman    schedule 13.02.2009

У меня были случаи (когда я выполнял foreach через коллекцию Table.Rows), когда использование var приводило к тому, что тип принадлежал к некоторому базовому классу, а не к фактическому типу DataRow. Это единственный раз, когда у меня были проблемы с var.

person Ries    schedule 13.02.2009
comment
Это потому, что коллекция строк в DataTable - это IEnumerable, а не IEnumerable<DataRow>. Когда вы указываете тип в цикле foreach, компилятор C # молча вставляет туда приведение. Это не проблема с var, вы просто стали жертвой старого API, который никогда не обновлялся для Generics. Попробуйте как-нибудь выполнить цикл foreach / var над table.Rows.Cast<DataRow>(), и вы увидите разницу. - person Joel Mueller; 05.04.2018

Вот результаты проведенного мной теста на эффективность var по сравнению с явной типизацией:

  private void btnVar_Click(object sender, EventArgs e)
    {
        Stopwatch obj = new Stopwatch();
        obj.Start();
        var test = "Test";
        test.GetType();
        obj.Stop();
        lblResults.Text = obj.Elapsed.ToString();
    }

    private void btnString_Click(object sender, EventArgs e)
    {
        Stopwatch obj = new Stopwatch();
        obj.Start();
        string test = "Test";
        obj.Stop();
        lblResults.Text = obj.Elapsed.ToString();

    }

Результат первого ярлыка: 00:00:00 000034

Результат второй метки: 00:00:00 00008

person AmelB    schedule 01.08.2012
comment
Вы можете добавить test.GetType(); ко второй функции. Во-вторых, щелкните как в прямом, так и в обратном порядке, чтобы учесть эффекты кэширования. В-третьих, выполните тест несколько раз и усредните. Без этого у вас будет бесполезный результат. - person Sjoerd; 02.08.2012
comment
Почему вы выполняете test.GetType(); первую, а не вторую функцию? Эта разница делает результаты несравнимыми. - person Spoike; 02.08.2012
comment
Если вы посмотрите MSIL, var фактически изменится на STRING .. Таким образом, две функции должны быть одинаковыми. - person Max; 27.09.2012