C# vs R#: использование var вместо явного указания типа



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

    Ответ был найден в блоге у создателей R# по этому адресу. По катом мой перевод статьи Ильи Рыженкова (Ilya Ryzhenkov).



    Итак, Илья приводит следующие доводы в пользу повсеместного использования «var»:
    • использование var потребуется вам для определения переменной с анонимным типом. Тут все просто — вы не сможете определить переменную анонимного типа без использования var;
    • использование var принуждает вас более грамотно называть сами переменные. Когда вы читаете определение переменной с явным типом, то получаете больше информации и что-нибудь типа «IUnitTestElement current» имеет смысл. Тем не менее, когда локальная переменная используется дальше, вы прочитаете «current», что потребует от вас больше времени понять что она означает. Использование «var currentElement» позволяет более быстро понимать переменную в любом месте кода;
    • использование var принуждает к более качественному API. Во-первых, вы получите оптимальные типы, когда позволяете компилятору получать самому тип возвращаемого значения метода или свойства. И еще вы вынуждены будете более правильно называть свои методы, чтобы они явно указывали на то, что возвращают;
    • использование var принуждает к инициализации переменных при их объявлении. В общем случае, инициализация переменных при определении является хорошим тоном, а в нашем случае, компилятор обязательно требует такую инициализация при определении переменной через var;
    • использование var приводит к уменьшению «шума» в коде. Существует множество случаев, когда объявленные неявно переменные уменьшают количество текста, который приходится читать разработчику и который он мог бы пропустить. Если мы не используем var, то определение переменной через выражение new или cast требует указание типа дважды. Когда мы имеем дела с обобщениями (generics), то такое положение дел приведет к появлению большого количества излишнего, чрезмерного кода (redundant code). Еще одним подобным примером может стать переменная итерации в foreach для типа наподобие «Dictionary<TKey,TValue>»;
    • использование var позволяет уменьшить использование директивы using. С var у вас нет явных ссылок на типы, и так как компилятор определит тип за вас, то вам не нужно импортировать пространства имен, когда вам требуется какая-нибудь временная переменная.

    Вот такое вот объяснение. Хотел бы привести еще один комментарий к этой статье. Alexander пишет что Микрософт не рекомендует использовать var нигде кроме как в случае анонимных типов. На что Илья отвечает просто: «Yeah, Microsoft often tries to make things „safer“. I don't agree with them here :)». Думаю перевод тут излишен.

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

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 57

      0
      Мы пока на работе используем 2005 студию и, хотя сам я и использую 2008, но приходится следить за совместимостью, поэтому var`ом не пользуюсь, как и другими прелестями C# 3.0.
      Для таких типов как int, double, decimal, string не вижу особого смысла использовать var (Это только путает), а вот при использовании new, удобство конечно ощутимое. Намного понятнее смотрится «var list = new List
        0
        Смысл для стандартных типов есть в виде 2 и 4 пункта: грамотное именование и обязательная инициализация. С другой стороны, ничего не мешает именовать и инициализировать тот же string самому и без var. Похоже, в этом случае смысл больше в философии, чем в конечной пользе.
          0
          2 пункт: Я и так всегда стараюсь использовать подробные имена.
          4 пункт: Это вообще спорный вопрос. Я в ASP.NET`е часто использую конструкции вида:
          int value;
          try { value = Convert.ToInt32(...); } catch { return null; }
          Если мне нужно объявить переменную без инициализации, почему бы мне это не сделать.

          Просто запись var value = 5; для меня неудобна. Я понимаю, что 5 имеет тип int, но тут нигде это не указано. поэтому тут я предпочту int value = 5;
            0
            между прочим, использование перехвата исключений очень неоптимально для конвертирования в int
            почему бы не использовать придуманный для этих целей Int32.TryParse()?

            по делу, я в целом с вами согласен, но очевидно, что в словах Ильи Ряженкова тоже есть доля смысла
              0
              Тут скорее дело вкуса. Главное, чтобы каждый потом сам разобрался в своём коде.

              По поводу TryParse, я учту. Я пока только учусь: о)

                +2
                если учитесь, то совет: старайтесь минимизировать использование исключений, особенно в таких банальных вещах
          +3
          Вопрос спорный, согласен. Лично я это правило в решарпере отключил и не использую вар для простых типов, а пользуюсь в редко, тк в противном случае код становится менее читаемым.
            0
            в этой статье, в том числе, написано, как с var добиться столь же читаемого кода, как и без var
            впрочем, я так же редко использую var, правда, теперь появилось желанее использовать его больше
            +1
            Практически всегда и везде использую var — это банально удобно.
            В особенности при обьявлении типа
            var list = new List<IMySuperPuperInterface>();
            Зачем читать то, что уже написано?

            Часто я банально не помню что возвращает тот или иной метод. Помню что что-то енумерабильное, причем какого-то конкретного типа, а какое — фиг знает.
            Если обьявлю переменную как IEnumerable, то рискую забыть про какой-то функционал, и в результате тип переменной придется сначала посмотреть, потом переопределить. С вар все проще, будет использовано ровно то, что есть.

            Про имена вопрос философский, имхо изменилось при использовании вара немного. Как были имена, так они и остались.

            Вот в целом чем мне кажется полезным var, поэтому его и использую.
            К конструкция типа
            var sum = 0; var text = "Hello, world!";
            уже привык. Мозг не страдает.
              +1
              Использование var я считаю большим преимуществом уже только ради пятого пункта. Шума меньше в разы. Читать заметно проще.
                0
                var превращает C# в питон :)
                хотя я пока ещё не писал на 3-м шарпе, мне кажется что повсеместное использование var — это плохо.

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

                var currentItemValue = «»;


                currentItemValue = 5;

                будет ли ошибка? будет ли число приведено к строке? или изменится тип переменной (как в питоне или руби)?

                в данном случае явное объявление string currentItemValue лучше.
                  0
                  не уверен про питон, но компилятор C# определит при инициализации currentItemValue как string и currentItemValue = 5;
                  вызовет ошибку компиляции. Программа даже не запустится. В C# var — это просто способ скрыть название типа тогда когда это имеет смысл.
                    –9
                    вот, значит будет ошибка.
                    неинтуитивно :) а в комплекте с начинающими программистами это способно вызвать очень много ошибок.
                      0
                      почему неинтуитивно?

                      это

                      string currentItemValue = «»;


                      currentItemValue = 5;

                      интуитивнее? кстати, если вы читали эту статью, то поняли бы что в связке с var переменную следует называть currentItemTextValue
                        0
                        где-то была статья, что не стоит городить огромные имена переменны. код станет ещё стращнее чем без var
                          –1
                          согласен, назовите переменную textValue
                    0
                    var в C# — это совершенно НЕ тип variant, который есть в некоторых языках
                      0
                      Var ничего общего с поздним связыванием не имеет. А начинающий программист вряд ли ожидает такой возможности. Тем более, что подобного рода ошибки выявляются на этапе компиляции. Проблем быть не должно.
                      –1
                      Resharper страшно тормозил всю систему оба раза которые я его ставил на протяжении последних 4 лет. После чего я им не пользуюсь, думаю что от использования var вместо явно подходящих для конкретного случая типа приведёт к тому же эффекту — замедлению исполнения кода.

                      C# — это всё таки Си, не надо делать из него Бейсик.
                        +1
                        еще раз говорю, var в C# — это не variant-тип
                          0
                          Насколько я понял, к замедлению исполнения кода это не должно привести. Все типы выводятся на этапе компиляции. Однако, хотя я еще и не писал на C# 3.0, читать код с использованием var мне, честно говоря, пока трудновато.
                            0
                            Просто картина становится похожа на javascript, с точки зрения читаемости кода.
                              0
                              ну все хорошо в меру, плохому программисту и var не надо чтобы все запутать
                              тезисы этой статьи ясно показывают почему var полезен в C#, сравнение с другими языками вообще считаю неуместным
                            0
                            Resharper в 4 версии шагнул далеко вперед по производительности.
                            +1
                            Илья очень просит изменить написание его фамилии, правильно будет: Рыженков.
                            Заранее спасибо!
                              0
                              сорри, исправил
                              а вы знакомы с Ильей? А может быть он появится на Хабре, я думаю было бы интересно как почитать его статьи так и пообщаться
                                0
                                Мы всегда рады пообщаться с нашими пользователями тут: www.intellij.net/forums/category.jspa? categoryID=6

                                К сожалению, просто не хватает времени быть везде. Но мы стараемся читать все, что пишут про нас в интернете и по возможности отвечать.
                              +1
                              Доводы нормальные, но надо понимать, что в Microsoft тоже не глупые люди работают, и если они что-то рекомендуют (или не рекомендуют) в отношении использования ИХ инструментов, то лучше прислушаться именно к ним.
                              Уверен, доводов у Microsoft не меньше, тот же Кшиштоф Квалина часто разбирает подобные спорные моменты.
                                +1
                                и именно поэтому в C# 4.0 будет late bound.

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

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

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

                                «Уменьшить использование директивы using» — это вообще непонятно зачем, кому они мешают? Я вот сейчас разбираюсь с крупными проектами, написанными до меня, так там открывая незнакомый класс, я останавливаю взгляд на этих самых директивах, чтобы нормально понимать с чем мне предстоит работать в этом классе.

                                Ещё сходу пример по этому поводу. Я, предположим, увидел следующий код:

                                var products = someDB.GetProducts();

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

                                IQuerable<Product> products = someDB.GetProducts();

                                Я бы сразу понял, что имею дело с IQuerable, и при желании без проблем щелкнул бы по «Product» правой кнопкой, Go to definition и наслаждаюсь разбором замысловатого класса Product. Согласитесь, что var все же совсем не облегчает эту работу.

                                Если резюмировать, то я склонен четко называть имена переменных и использовать var в 99% случаев только с анонимными типами. Автор этих доводов, бесспорно, опытный человек, но некоторые пункты притянуты зауши, имхо, чтобы было больше пунктов (как с using, например).

                                PS. Сори за многобуков, и небольшой сумбур в изложении.
                                  +1
                                  Согласен, var хоть и удобная, но весьма вредная «привычка». Не тот язык C# чтобы засорять его подобными фенечками. С# прост и прямолинеен — как танк. А танку «бантики» не нужны :)

                                  PS: Безусловно, для анонимных типов var необходим, но не более того.
                                    0
                                    ошибся кнопкой, см. пример кода ниже
                                    0
                                    Мне кажется твоя и Ильи точки зрения просто параллельны друг другу, то есть не мешают. На практике можно брать чуток из одной, чуток из другой. Мне понятна и та и другая. До того, как я прочитал эту статью, у меня на видение проблемы была только одна точка зрения — «var» использовать пореже. Теперь в сознании появилось разнообразие.
                                      0
                                      Не мешают они до тех пор, пока он «там» себе что-то пишет, а я «здесь». А если бы мы в одной компании работали? :)

                                      За примером даже далеко ходить не нужно. Взять кусочек кода в самом конце статьи о Expression Trees, там где замеры делаются. Вот мне этот элементарный код не очевиден, я его не могу просто одним взглядом прочесть, что куда присваивается, что возвращается и т.д. Хотя примеров на C# я уже перечитал достаточно, чтобы набить руку и сходу понимать. Даже в цикле for и то var написано. В тоже время если бы <~legigor> читал мой код — ему бы «рябило».

                                      Так что эти два стиля программирования вполне могут быть и сосуществовать, пока их пользователи не пересекаются :)
                                        0
                                        для компаний естественным выбором является написание внутреннего (или принятие какого-то существующего) соглашения об оформлении кода и тогда таких проблем как правило не возникает
                                          0
                                          Тому кто позже в неё попадет — придется ломать стереотипы ))
                                            0
                                            к сожалению, к компаниях, иной раз, сложнее поломать другие стереотипы типа дрескода или корпоративной этики
                                    –4
                                    Вот простой пример кода, когда тип не является анонимным, но явно его указать нельзя.

                                    class A<T>
                                    {
                                     T myField;

                                     void Foo<T>(T p)
                                     {
                                      var v = myField;
                                     }
                                    }

                                    * This source code was highlighted with Source Code Highlighter.


                                    А «var» будет и в новом C++ — в языке концептуально более строгом.
                                      0
                                      может я чего то не понял, но что мешает написать:

                                      void Foo(T p)
                                      {
                                      T v = myField;
                                      }
                                        0
                                        тем, что T — разные. (и не Foo, а Foo)
                                          0
                                          угловые скобки пропали, как понимаю, и в вашем и в моем варианте.

                                            +2
                                            тогда наверное стоит тип в определении шаблонного метода называть не T
                                          0
                                          Код работать не будет. Скажет, что-то в духе «не понимаю, какой нахер T»?
                                            0
                                            Т понятно какой, из декларации метода, но ожидается как раз Т из декларации класса.
                                              0
                                              Да, именно так и говорит.

                                              Народ тут спорит о самых тонкостях правильно или нет использовать var, но, согласитесь, за такой код как вы в примере написали, нужно бить по пальцам.
                                              Если var counter = 25; может быть и не очевидно, то такая путаница типов вообще страшно выглядит.

                                              Разрешение ошибки компилятора в данном случае с помощью var — вообще как баг выглядит, и его как аргумент приводить неправильно.
                                                0
                                                на самом деле все это религиозные войны

                                                со своей стороны могу несколько доводов в пользу var привести:

                                                1) Зачастую важен не тип, а его содержание, а Go to type declaration работает и на var и не на var
                                                2) Зачастую важен не тип, а семантика — а это уже задача грамотно написанного локального кода, хороших имен методов и проч.
                                                3) var спасет от головной боли в случае массовых рефакторингов и изменений в иерархиях типов
                                                4) вы все равно полагаетесь на компилятор в плане проверки типов, пусть он еще и выводит их, заодно будет работать комплишн (актуально если декларация сужает возвращаемый тип)

                                                а counter не бывает типа string, так что var counter очевидность не снижает
                                                и главное, что строчки с var чудом влезают по ширине в экран монитора — вот за это уже можно отдать все, и черт с ними, остальными пунктами!
                                                0
                                                Тем не менее, работать не будет:)
                                                  0
                                                  Warning 2 Type parameter 'T' has the same name as the type parameter from outer type 'A'
                                                  Это ли не повод задуматься?:)
                                                    0
                                                    конечно повод :)
                                                    но в большом проекте обычно столько варнингов!
                                                    опять же, если код, например, автогениренный, то никто даже не обратит внимания.
                                              0
                                              вар очень хорош для уменьшения шума в коде, но, конечно, применять его надо с умом :) в общем то, нет смысла спорить о варе, потому что, если только заменять обычное определение типов, на процесс исполнения это не влияет, определяющим здесь является стиль программиста ) а еще очень интересно, нет ли у Микрософта стилистических рекомендаций по написанию кода для 3.0?
                                                0
                                                Рекомендации можно почитать, например, здесь. Автор статьи, Peter Ritchie, хоть и не из Microsoft, но c MVP статусом, так что можно ему верить :)
                                                0
                                                Ещё случай, когда var предпочтительней:

                                                Пусть есть ASP.NET Web Site, в нём подпапка SomeFolder, в ней — папка Controls, в ней контрол UserPreview.ascx. Имя класса для этого контрола будет иметь вид: SomeFolder_Controls_UserPreview.

                                                И где-нибудь в обработчике события ItemDataBound (у Repeater-a, например) при получении ссылок на эти User-контролы внутри ItemTemplate мы напишем:

                                                var userProfile = (SomeFolder_Controls_UserPreview)e.Item.FindControl(«userProfile»);

                                                Дублировать тут это длинное автогенерируемое имя дважды — явный шум.

                                                Ну а с другой стороны:
                                                int usersCount = 0;
                                                немного предпочтительнее всё же, чем
                                                var usersCount = 0;

                                                Все остальные случаи — где-то между.
                                                  0
                                                  в статье ваш случай описан в пятом пункте
                                                    0
                                                    не спорю :) там просто конкретизировано про generics, а я тут немного из другой сферы решил добавить
                                                      0
                                                      однако, слово cast там даже выделено стронгом :)
                                                        0
                                                        но за пример спасибо, наглядно для тех, кто не понял
                                                    0
                                                    Хороший пример, когда бездумное применение var приводит к косякам:

                                                    var anObj = new SomeComponent();
                                                    Assert.Taht(anObj.Property1, Is.True);

                                                    вместо явного указания такого плана

                                                    ISomeComponent anObj = new SomeComponent();
                                                    Assert.Taht(anObj.Property1, Is.True);

                                                    Only users with full accounts can post comments. Log in, please.