Как неявная типизация делает код более понятным?

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

var words = new[] { "a", "b", null, "d" };

foreach (var item in words)
{
    Console.WriteLine(item);
}

Мне кажется, что верно и обратное: если бы вы использовали вместо этого string , то читатели кода сразу узнали бы, что это была строка в цикле foreach, вместо того, чтобы искать в коде, где определена переменная.

Как неявная типизация делает приведенный выше код более понятным?

Приложение

Книга C # 3.0 - Die Neuerungen. schnell + kompakt на немецком языке, фактический текст:

Das Schluesselwort var kann auch beim Durchlaufen von foreach-Schleifen verwendet werden, um somit den Code uebersichtlicher und einfacher zu gestalten. Besonders bei complexen Typen kann man auf diese Art und Weise Programmierfehler verhindern.

Вот мой перевод:

Ключевое слово var также можно использовать при переборе циклов foreach, что делает код проще и проще для создания. Особенно при использовании сложных типов, это может предотвратить ошибки программирования.

Хорошо, читая его более внимательно, он фактически заявляет, что var в цикле foreach делает код проще для создания , но не обязательно легче читать.

10
задан Edward Tanguay 6 August 2010 в 16:26
поделиться

13 ответов

Использование неявной типизации - это руководство, а не закон.

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

2
ответ дан 3 December 2019 в 13:27
поделиться

Мне кажется странным, что только программисты на C # и Java страдают от недуга, не позволяющего им извлекать информацию из контекста кода, в то время как разработчики Python, JavaScript, Ruby, F #, Haskell и другие кажутся невосприимчивыми к этому. Почему кажется, что у них все в порядке, а нам, программистам на C #, необходимо это обсуждение?

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

Вывод типа в статически типизированных языках должен быть нормой, а не исключением; он уменьшает визуальный беспорядок и избыточность, делая ваше намерение более ясным, когда вы делаете явно указываете тип, потому что вам нужен менее производный тип ( IList list = new List (); ).

Кто-то может привести доводы против var следующим образом:

var c = SomeMethod();

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

Улучшено:

var customer = SomeMethod();

Лучше:

var customer = GetCustomer();

Давайте попробуем ввести явный тип:

Customer customer = GetCustomer();

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

Возможно, некоторые противники var могут согласиться с тем, что в приведенном выше примере var не причиняет вреда. Но что, если метод возвращает не простой и хорошо известный тип, например Customer или Order , а какое-то обработанное значение, например какой-то словарь? Что-то вроде:

var ordersPerCustomer = GetOrdersPerCustomer();

Я не знаю, что это вернет, это может быть словарь, список, массив, что угодно на самом деле. Но какое это имеет значение? Из кода я могу сделать вывод, что у меня будет итеративная коллекция клиентов, где каждый Customer , в свою очередь, содержит итеративную коллекцию Order . Меня действительно не волнует тип здесь. Я знаю, что мне нужно знать, если окажется, что я ошибаюсь, это ошибка метода, который ввел меня в заблуждение своим именем, что не может быть исправлено явным объявлением типа.

Давайте посмотрим на явную версию:

IEnumerable<IGrouping<Customer,Order>> ordersPerCustomer = GetOrdersPerCustomer();

Я не знаю, как вы, но мне гораздо труднее извлечь из нее нужную мне информацию. Не в последнюю очередь потому, что бит, содержащий фактическую информацию (имя переменной), находится дальше правее, где моим глазам потребуется больше времени, чтобы найти его, он визуально закрыт всеми этими сумасшедшими < и ]> . Фактический тип ничего не стоит, это чепуха, особенно потому, что чтобы разобраться в нем, вам нужно знать, что делают эти общие типы.

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

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

Тем не менее, я обычно использую явную типизацию для «примитивов», таких как int и string . Но, честно говоря,это скорее привычка, а не осознанное решение. Однако с числами вывод типа может вас испортить, если вы забудете добавить m к буквальному числу, которое вы хотите ввести как десятичное , что достаточно просто сделать, но компилятор не позволит вам случайно потерять точность, так что это не актуальная проблема. Фактически, если бы я везде использовал var , это значительно упростило бы изменение величин в большом приложении, над которым я работаю, с целых на десятичные числа.

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

11
ответ дан 3 December 2019 в 13:27
поделиться

Не думаю, что я действительно понимал достоинства неявной типизации в C #, пока не начал использовать F #. В F # есть механизм вывода типа, который в некотором смысле похож на неявную типизацию в C #. В F # использование вывода типа - очень важный аспект разработки кода, который действительно может сделать код более читаемым даже в простых примерах. Обучение использованию вывода типов в F # помогло мне разобраться в тех ситуациях, когда неявная типизация может сделать C # более читабельным, а не более запутанным. (Случай Linq, на мой взгляд, довольно очевиден, но многие случаи - нет.)

Я понимаю, что это больше похоже на плагин для F #, чем на ответ на вопрос re.C #, но это не то, что я могу сказать вплоть до простого набора правил. Это умение смотреть на код по-новому, когда думаешь о таких вещах, как удобочитаемость и обслуживание. (Это, и, может быть, это что-то вроде плагина для F #, лол.)

2
ответ дан 3 December 2019 в 13:27
поделиться

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

var words = new List<String>();

Мне нравится var, но я бы не стал использовать его, как в вашем примере, поскольку неясно, что это за тип.

8
ответ дан 3 December 2019 в 13:27
поделиться

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

var myClass = new ExtremelyLongClassNameIWouldntWantToTypeTwice();
11
ответ дан 3 December 2019 в 13:27
поделиться

Это более понятно, в смысле меньше шума/избыточности. Тип слова можно легко определить с помощью new[] { ... } как компилятором так и разработчиком . Поэтому var используется вместо string[], так как последний может визуально загромождать код.

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

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

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

Правка относительно вашего дополнения:

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

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

var words = new[] { new Word("a"), new Word("b"), null, new Word("d") };

// The compiler will complain about the conversion from Word to string,
// unless you have an implicit converion.
foreach (string word in words)
{
    Console.WriteLine(word);
}

Если мы do используем var, код скомпилируется без ошибок, но вывод программы будет совершенно другим, если класс Word не реализовал (правильно) ToString().

var words = new[] { new Word("a"), new Word("b"), null, new Word("d") };

foreach (var word in words)
{
    Console.WriteLine(word); // Output will be different.
}

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

5
ответ дан 3 December 2019 в 13:27
поделиться

Если вы хотите сделать этот код более явным, я бы предложил расширить новый вместо удаления var:

var words = new string[] { "a", "b", null, "d" };

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

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

ISpecificInterface foo = new YourImplementation()
1
ответ дан 3 December 2019 в 13:27
поделиться

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

Есть два случая, когда вы можете захотеть использовать var, и один, когда вы должны это сделать:

Где это может понадобиться:

  1. Это может быть полезно в экспериментальном коде, когда вы быстро переключаете типы, исследуя проблемное пространство.
  2. Он может быть полезен при работе со сложными типами на основе generic, такими как IGrouping>>>, что особенно часто случается с промежуточными состояниями в сложных запросах и операциях перечисления.

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

Где это нужно:

При работе с анонимными типами, в коде типа:

var res = from item in src select new {item.ID, item.Name};    
foreach(var i in res)    
    doSomething(i.ID, i.Name);

Здесь res - IEnumerable или IQueryable анонимного типа, а i - этот анонимный тип. Поскольку тип не имеет имени, его невозможно явно объявить.

В последнем случае это не синтаксический сахар, а жизненно необходимый элемент.

С этим связано то, что раньше в SharpDevelop была своя собственная форма var при редактировании; можно было набрать:

? words = new string[] { "a", "b", null, "d" };

И после запятой редактор выдавал:

string[] words = new string[] { "a", "b", null, "d" };

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

3
ответ дан 3 December 2019 в 13:27
поделиться

Это делает код более понятным, когда

  1. у вас есть класс с очень длинным именем.
  2. У вас есть linq-запросы.
2
ответ дан 3 December 2019 в 13:27
поделиться

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

И основные преимущества неявной типизации:

  • Уменьшает текст программы, особенно для длинных имен типов (классы, интерфейсы), таким образом улучшая читаемость
  • Вызывает меньше изменений при изменении типа возвращаемого значения метода.

Похоже, оба варианта улучшают читаемость.

Думаю, это зависит от ваших предпочтений и, возможно, также от руководящих принципов программирования в вашей команде. Благодаря текущей поддержке инструментов (рефакторинга) в среде IDE стало намного проще изменять имена типов (это понятно), поэтому причина того, что неявная типизация сокращает количество изменений, практически исчезла с точки зрения усилий.

Я бы посоветовал: делайте то, что лучше всего подходит для вас. Нет правильного или неправильного. Попробуйте какое-то время каждый подход (например, настроив параметры вашего любимого инструмента рефакторинга), а затем используйте то, что облегчает вашу жизнь как разработчика.

0
ответ дан 3 December 2019 в 13:27
поделиться

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

var, однако, сияет при работе с большими иерархиями наследования и шаблонами. Вы также можете прочитать это как "Мне все равно - просто дайте мне данные" Хотя шаблоны в C# не обладают выразительной силой своего аналога в C++, они обладают большей выразительной силой, чем дженерики в Java, и это означает, что люди могут создавать конструкции, которые не только неудобны, но и труднорасполагаемы, если вы должны явно указывать точный тип.

Например, представьте себе шаблонную обертку вокруг нескольких типов DataReader-ов для общения с SQL - так, чтобы вы все еще могли быть эффективными (вызвать sproc, получить результаты, done), но без бремени ведения домашнего хозяйства (закрытие ридера и соединения, повторные попытки или ошибки и т.д.). Код, который использует эту функцию, будет вызывать только одну функцию, с максимально коротким синтаксисом, и она будет возвращать обертку, которая будет действовать как умный указатель в C++ - действовать как DataReader для вашего кода, но также обрабатывать все побочные вещи. Так что все выглядит просто:

using (var r = S.ExecuteReader("[MySproc]", params))
{
    while ((+r).Read())
    (
       // get my data and be done
    )
} // at this point wrapper cleans up everything

В подобном случае вам не только не важно, как названа и объявлена обертка, вам даже не важно знать ее имя - для вашего кода это не имеет значения. Вы просто хотите получить свои чертовы данные и идти дальше :-), не разбираясь ни с чьими длинными декларациями.

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

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

О, и даже редактор VS выведет для вас полный тип, предложит автозавершение и будет жаловаться, если вы попытаетесь использовать что-то, что он не может сделать, так что var не нарушает безопасность типов вообще (новый C++ также получил свой эквивалент var - давно пора).

0
ответ дан 3 December 2019 в 13:27
поделиться

Чистый здесь означает менее избыточный . Поскольку компилятору тривиально сделать вывод о том, что тип объекта - строка [] , его явное указание считается многословным. Однако, как вы отметили, для человека, читающего код, это может быть не так очевидно.

3
ответ дан 3 December 2019 в 13:27
поделиться

Когда вам нужно изменить код, вам придется делать это в меньшем количестве мест. Больше нет словаря или более длинных объявлений типов, не больше Some.Very.Long.Class.With.Very.Long.Path объявление = functionParameter [index] и т. Д.

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

0
ответ дан 3 December 2019 в 13:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: