7

Изучая Entity Framework натолкнулся на интересную вещь :

Для создания запроса мы можем использовать var :

var phones = db.Phones.Where(p=> p.Company.Name=="Samsung");

либо явно указывая тип запроса ,например :

Phone myphone = db.Phones.Where(p=> p.Company.Name=="Samsung");

что меня натолкнуло на интересный вопрос :

А можно ли при разработке использовать при создании переменных в большинстве случаев var вместо явного указания типа?Если нет, то почему нежелательно?

А теперь наведу несколько примеров ,в которых указаны различия между использованием var явным указанием типов,почему мне кажется что все-таки var в большинстве случаев будет юзабельней и читабельней для разработчиков :

1) Для объявления и дальнейшего использования таких структур как списки и словари(к примеру) которые имеют в себе уже вложенные типы:

Dictionary<Dictionary<String, String>, String> items = new Dictionary<Dictionary<String, String>, String>();

Соответственно через var :

var items = new Dictionary<Dictionary<String, String>, String>();

(Ссылка на вопрос : https://stackoverflow.com/questions/10572693/why-use-var-instead-of-the-class-name)

2) Использование var как итератор при перечислениях в коллекциях :

    class Item {
  public string Name; 
}
foreach ( Item x in col ) {
  Console.WriteLine(x.Name);
}

Код выше - скомпилируется без ошибок ,но при исполнении программы мы получим ошибку приведения типов . Потому что : сам цикл foreach может работать с обоими интерфейсами IEnumerable и IEnumrable<T> . Здесь же результат исполнения имеет тип object и C# компилятор кастит(приводит) это к типу Item . Следовательно , это небезопасно и может привести к ошибке работы программы ,потому что интерфейс IEnumerable может содержать объекты абсолютно любого типа.

Теперь вариант с var :

    foreach ( var x in col ) {
  Console.WriteLine(x.Name);
}

Здесь же тип x будет object если интерфейс IEnumerable и T если используется IEnumerable<T>

(Ссылка на вопрос : https://stackoverflow.com/questions/3425966/what-advantages-does-using-var-have-over-the-explicit-type-in-c)

3) И само собой - использование var при запросах + либо получение объекта с уже определенным типом :

 var l = new List<string>(); // думаю ,очевидно какой тип будет у var
 var s = new SomeClass(); //
 var results = from r in dataContext.SomeTable select r; // Получение выборки из БД(путем LINQ To Enities)

Или запрос через подход LINQ To Objects :

var results = from item in someList
          where item != 3
          select item;

UPD: ну и пример с использованием анонимных типов :

var test = new {Id = 5,Name="Joseph"}

var result = context.MyTable.Select(x=>new {x.Id,x.Name}).ToArray();

(Ссылка на вопрос : https://stackoverflow.com/questions/41479/use-of-var-keyword-in-c-sharp?rq=1)

Краткий вывод из примеров выше : var - достаточно неплохая вещь ,поскольку он строго типизирован + правильное название переменных позволяет ещё и повысить читабельность . Что в суме значительно повышает эффективность кода без ущерба для безопасности

Вопрос в том : есть ли преимущества использования явного указания типов при объявлении переменных перед var?

Я просто новатор в этом деле и хотелось бы услышать мнение опытных людей.Спасибо за конструктивный ответ!

clyde
  • 488
  • Ещё можно добавить: использование var при выражениях с анонимными типами (когда тип результата придумает компилятор). Хотя сам вопрос, когда использовать var, а когда нет - дело холиварное, есть много доводов за и против. Потому ответ один- используйте то, что решает задачу и что предпочтительно в вашем проекте/команде/компании – tym32167 Sep 13 '17 at 13:33
  • 2
    Я просто новатор в этом деле :) новатор? – tym32167 Sep 13 '17 at 13:34
  • Опыта у меня не так много, но, var/auto полезны только тех местах где не хочется дублировать типы, однако, бывают случаи, на вроде: var data = DataFactory.getValue(); И вот попробуйте понять, какого у вас типа data смотря в код, не используя подсказки IDE :D Аналогично с плюсовым auto. Так что, в остальном, у этих операторов есть плюсы и удобства. – test123 Sep 13 '17 at 13:35
  • Статья на сайте Microsoft. Там рекомендации, когда лучше использовать var. – EvgeniyZ Sep 13 '17 at 13:36
  • 1
    @EvgeniyZ ну, если говорить о литературе, то есть книга "Инфраструктура программных проектов" (на англ она бесплатная - Framework Design Guidelines) - там описано когда что использовать. Но это все не свод жестких правил, просто рекомендации – tym32167 Sep 13 '17 at 13:41
  • 1
    @test123 Как я говорил, есть много за и против, это нескончаемый холивар. – tym32167 Sep 13 '17 at 13:42
  • @tym32167 имел ввиду новенький) – clyde Sep 13 '17 at 13:48
  • @EvgeniyZ просто было у меня было предположение что это больше от компаний зависит ,возможно ,политика которых ограничивает использование var и тд – clyde Sep 13 '17 at 13:50
  • @tym32167 там есть пример где var l = new List<string>(); - что равноценно List<string> l = new List<string>(); – clyde Sep 13 '17 at 13:52
  • @EvgeniyZ спасибо большое за ссылку ,но в вопросе ,я уже пытался высветить похожие моменты – clyde Sep 13 '17 at 13:54
  • @clyde я писал про анонимные типы, а не про генерики. var zzz = new {Id = "asdasd", Name = "asdasd"}; – tym32167 Sep 13 '17 at 13:58
  • @tym32167 понял ,добавлю пожалуй в примеры) – clyde Sep 13 '17 at 14:00
  • @clyde У тебя же linq в тегах, вот и используй linq для примера с анонимными типами, например var result = context.MyTable.Select(x=>new {x.Id,x.Name}).ToArray(); – tym32167 Sep 13 '17 at 14:19
  • Найдите дубликат, кто-нибудь! Эта тема уже перетиралась на сайте, и кажется не один раз. – VladD Sep 13 '17 at 14:23
  • 1
    Возможный дубликат вопроса: var или Имя класса или псевдоним? – tym32167 Sep 13 '17 at 14:25
  • @VladD на все дубликаты, что я нашел, ты и отвечал :) – tym32167 Sep 13 '17 at 14:26
  • @tym32167: Наверняка :) Но я с телефона, искать не так просто. – VladD Sep 13 '17 at 14:28
  • 2
    @tym32167 там дубликат о невкусовых предпочтениях. это вопрос - о вкусовых :) –  Sep 13 '17 at 16:27

3 Answers3

9

var - это ключевое слово для компилятора, а не для человека. Хорошее* использвование var - это когда вы хотите сказать компилятору:

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

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

Вот ты пытаешься починить в моем коде хитрый баг, набрал себе в голову те 5-7 вещей которые можешь удержать. А теперь выбрось их и начни выяснять тип этой переменной!


Стандартное правило:

  1. Можно писать var, если при чтении кода человеком тип переменной прямо виден из контекста:

    • тип переменной явно написан в той же строчке: var a = new B()
    • тип переменной неявно написан в той же строчке:

      var some = BFacrory.Create();
      var tasks = dataContext.Tasks;
      
    • тип прямо следует из кода

      foreach (var task in dataContext.Tasks) ...
      
    • и еще куча случаев, которые можно описать как "тип очевиден".

  2. Можно писать var, если тип переменной не важен человеку, читающему код. Например, при использовании анонимных типов в цепочке LINQ-запросов. В этом случае важен не конкретный тип, а поля, которые в нем есть - а набор полей прямо виден из контекста.

  3. Можно писать var, если ваш код не будет читать никто и никогда. Например, если это временный набросок, который никогда-никогда не станет постоянным.

  4. Нельзя писать var во всех остальных случаях.


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

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

* По мнению автора ответа. Почувствуйте себя человеком, который вынужден перестать читать код ради выяснения, что ж это за var^Wзвездочка.

  • Ответ дейвствительно очень исчерпывающий ,спасибо ! – clyde Sep 13 '17 at 16:21
  • А я дочитал до конца игнорируя звёздочки и никаккого дискомфорта не испытал :) – Qwertiy Sep 13 '17 at 18:50
  • @Qwertiy значит читал невнимательно! –  Sep 13 '17 at 19:02
  • Внимательно. Даже обнаружил, что словарь засунули в ключ :) – Qwertiy Sep 13 '17 at 19:09
6

Код должен хорошо читаться. Если var способствует этому - используй его. Если нет - не используй. Всё просто.

Если из-за обилия var ты не понимаешь в какой момент времени какой тип у тебя хранится в той или иной переменной - не используй var. В противном случае - почему бы и нет?

Пример с Dictionary плох тем, что наличие таких generic'ов в проекте - уже само по себе проблема.

  • 1
    Код должен не просто хорошо читаться, но быть расширяемым, тестируемым, поддерживаемым, работающим и тд и тп. С таким же успехом можно ответить "Юзай var чтобы сделать лучше, если лучше не получается, не юзай var" – tym32167 Sep 13 '17 at 14:01
  • @tym32167 Каким образом синтаксический сахар относится ко всему описанному тобой? Это же не dynamic. А твоя формулировка совершенно правильная. Если var мешает, значит ему здесь не место. Если не мешает - значит, в самый раз. – Lunar Whisper Sep 13 '17 at 14:04
  • @LunarWhisper мне кажется просто при разработке , разраб должен отчетливо понимать какой тип будет получать даный var - ибо проблема определения его типа не должна вообще возникать ,если уж человек осознанно его использует – clyde Sep 13 '17 at 14:09
  • Я просто хочу сказать, что твой ответ очень общий, а аргументы спорные. В каких случаях тебе надо знать все типы всех переменных в коде, что ты читаешь? Если тебе надо понять, что делает функция - то это должно быть ясно из её сигнатуры. Если надо понять как она это делает - это должно быть немного строчек кода, так как функции должны быть короткие. Если надо понять требования к функции - смотри юнит тесты. То есть, по сути, реальных ситуаций, когда надо знать тип переменной во время чтения кода, должно быть мало. А во время написания ты и так должен знать все типы, что используешь – tym32167 Sep 13 '17 at 14:09
  • Я потому и говорю, что вопрос холиварный, конкретного ответа на него нет, есть только общий - делай хорошо, не делай плохо. – tym32167 Sep 13 '17 at 14:11
  • @LunarWhisper, >>Пример с Dictionary плох тем, что наличие таких generic'ов в проекте - уже само по себе проблема.<< А можете пояснить? – test123 Sep 13 '17 at 14:32
  • @LunarWhisper Да, ещё, использование var или конкретного типа может влиять на то, как программа себя поведет. То есть это такие не чисто сахар, это сахар с мозгами :) – tym32167 Sep 13 '17 at 14:33
  • @test123 код должен быть читаемый и понятный. Это Dictionary<Dictionary<String, String>, String> - нечитаемо и непонятно :) – tym32167 Sep 13 '17 at 14:48
  • код должен быть читаемый и понятный<< А как быть, если под капотом такая гирлянда есть, и жёсткая параметризация спасает от ошибок на стадии компиляции? Допустим, я вижу IMyObject item = ...; А по факту, этот объект типизирован каким нибудь <T<K,V>,<I,N>> ? При этом программист об этих внутренностях даже не знает.

    – test123 Sep 13 '17 at 15:02
  • @tym32167, эм.. словарь в качестве ключа словаря? Кажется, тебя надо String переставить в другое место :) И вообще, что же непонятного, если даже при чтении комментария заметно косяк?) – Qwertiy Sep 13 '17 at 18:52
  • @Qwertiy воу воу, я просто пояснил это Пример с Dictionary плох тем, что наличие таких generic'ов в проекте - уже само по себе проблема. Хотя я как то работал с проектом у которого уровень вложенности словаря в словарь был около 6-7 (точно не помню), ох я тогда намучился догонять для чего это и как работает :) – tym32167 Sep 13 '17 at 20:12
  • @tym32167 ты как-то упустил вопрос поддержки кода. А var - это одно из надёжных средств достижения write-only кода. Когда я вижу, что чужой код работает неверно, ищу ошибку, и не вижу ни одного типа, я даже не могу предположить - в чём ошибка. Например: var a = GetX(); var b = GetY(); var c = a / b; return GetZ(c); Я понятия не имею, происходит работа с целыми числами или дробными. Или там вообще произвольный тип у которого переопределен оператор деления. Хотя код то вот простой - 4 строчки. – Lunar Whisper Sep 14 '17 at 09:50
  • дак я ж написал вроде, что есть и за и против, что это холивар вечный, а ты споришь дальше. Есть куча за и против. Нет конкретных рекомендаций когда что использовать. Есть только общие рекомендации. Конечно ты найдешь пример, когда var вреден. А я найду пример, когда тип выражения указать невозможно или не имеет смысла. Ну и что толку от этого? – tym32167 Sep 14 '17 at 09:57
  • tym32167 тогда мне непонятна твоя позиция. Я же ответил, хорошо - используй, плохо - не используй. Это как раз самый верный ответ в данном случае. – Lunar Whisper Sep 14 '17 at 10:06
  • @test123 в случае таких страшных Generic'ов заводят специализированные коллекции и их элементы, которые скрывают подобную логику, а не выпячивают её в public API. Если внутри одного словаря лежит другой, наверняка при работе с таким страшилищем будет куча логики, которая создает вложенные Dictionary, добавляет элемент по двум ключам и т.д. - вся она уезжает в кастомную коллекцию. – Lunar Whisper Sep 14 '17 at 10:09
  • Моя позиция проста как тапок - на поставленный вопрос конкретного ответа нет, есть только общие рекомендации. Это я не тебе писал, а автору вопроса. :) – tym32167 Sep 14 '17 at 10:12
  • @LunarWhisper, Понял. Действительно) Меня вчера заклинило, сам же написал что есть IMyObject, и такое определение что с var-ом, что без него - всё равно читаемо. А то что находится внутри и то что не видимо - к вопросу о var не имеет отношения. В общем, я сглупил... – test123 Sep 14 '17 at 10:40
2

Правило на самом деле очень простое: код должен легко читаться, пониматься и отлаживаться, как вами, так и вашими последователями. Всё, что служит этому — хорошо и правильно, всё, что препятствует этому — плохо и неправильно.

Когда у вас стоит выбор между явным именем типа или var (при условии, что нет разницы для программы), подумайте, интересен ли читателю в этой точке точный тип, или нет. Если без указания точного типа текст программы не проиграет в ясности, смело пишите var. Не стоит утомлять читателя ненужными подробностями: знание того, что объект имеет тип List<Task<byte[]>>.Enumerator, обычно не нужно. Но если с var читателю придётся угадывать нужный тип, и этот самый конкретный тип важен, укажите его.

В моей практике, чаще всего точный тип неважен: читателю в большинстве случаев всё равно, имеет ли id тип string, int или Id. Но в тех немногих местах, где с id производятся вычисления, точный тип может оказаться важен. Вот в этом-то случае и нужно его указывать явно.

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


Да, а ещё есть случаи, когда язык за вас решает, использовать var или указывать тип. Например, для анонимных типов можно использовать только var (вне контекста обращённых методов, конечно).

VladD
  • 206,799