Как неявная типизация делает код более понятным? - PullRequest
10 голосов
/ 06 августа 2010

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

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

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

Мне кажется, что все наоборотtrue: если вы использовали 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.Besonders bei komplexen Typen kann man auf diese Art und Weise Programmierfehler verhindern.

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

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

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

Ответы [ 13 ]

11 голосов
/ 07 августа 2010

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

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

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

Некоторые могут возразить против var, как это:

var c = SomeMethod();

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

Улучшено:

var customer = SomeMethod();

Лучше:

var customer = GetCustomer();

Попробуем явный ввод:

Customer customer = GetCustomer();

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

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

var ordersPerCustomer = GetOrdersPerCustomer();

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

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

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

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

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

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

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

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

11 голосов
/ 06 августа 2010

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

var myClass = new ExtremelyLongClassNameIWouldntWantToTypeTwice();
8 голосов
/ 06 августа 2010

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

var words = new List<String>();

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

5 голосов
/ 06 августа 2010

Это яснее, в смысле меньше шума / избыточности .Тип words может быть легко определен с помощью оператора 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);
}

Если мы делаем используем var, код скомпилируется без ошибок, но вывод программы будет совершенно другим, если класс Word имеет't (правильно) реализовано 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 могут возникать тонкие ошибки, которые в противном случае были бы обнаружены компилятором.

3 голосов
/ 06 августа 2010

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

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

Где вы, возможно, захотите:

  1. Это может быть полезно в экспериментальном коде, когда вы быстро переключаетесь между задействованными типами во время исследования вашей проблемы.пробел.
  2. Это может быть полезно со сложными родовыми типами, такими как IGrouping<int, IEnumerable<IGrouping<Uri, IGrouping<int, string>>>>, что может особенно происходить с промежуточными состояниями в сложных запросах и операциях перечисления.

Лично я предпочитаюиспользовать даже сложную форму поверх 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 анонимного типа, а я - этого анонимного типа.Поскольку у типа нет имени, его невозможно явно объявить.

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

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

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

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

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

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

3 голосов
/ 06 августа 2010

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

2 голосов
/ 06 августа 2010

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

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

2 голосов
/ 06 августа 2010

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

  1. У вас есть класс с очень длинным именем.
  2. У вас есть запросы linq.
2 голосов
/ 06 августа 2010

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

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

1 голос
/ 06 августа 2010

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

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

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

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

ISpecificInterface foo = new YourImplementation()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...