Использование типа "var" в объявлении переменной - PullRequest
37 голосов
/ 07 сентября 2010

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

Мне не известно о каких-либо различиях между явным объявлением типа и использованием var после компиляции кода в MSIL.

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

Ответы [ 18 ]

50 голосов
/ 07 сентября 2010

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

double GetTheNumber()
{
    // get the important number from somewhere
}

А потом в другом месте ...

var theNumber = GetTheNumber();
DoSomethingImportant(theNumber / 5);

И затем, в какой-то момент в будущем, кто-то замечает, что GetTheNumber только когда-либо возвращает целые числа, так что рефакторинг его возвращает int вместо double.

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

Сказав это, подобные вещи должны быть обнаружены вашими юнит-тестами и т. Д., Но это все еще потенциальная ошибка.

22 голосов
/ 07 сентября 2010

Я склонен следовать этой схеме:

var myObject = new MyObject(); // OK as the type is clear

var myObject = otherObject.SomeMethod(); // Bad as the return type is not clear

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

14 голосов
/ 07 сентября 2010

Некоторые случаи действительно могут привести к неожиданным результатам. Я сам фанат var, но это может пойти не так:

var myDouble = 2;
var myHalf = 1 / myDouble;

Очевидно, что это ошибка, а не «неожиданный результат». Но это это ГАЧКА ...

13 голосов
/ 07 сентября 2010

var не является динамическим типом, это просто синтаксический сахар .Единственное исключение - анонимные типы. Из документов Microsoft

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

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

Из документации Microsoft (снова)

Неявно типизированныйЛокальная переменная строго типизирована, как если бы вы сами объявили тип, но компилятор определяет тип

В некоторых случаях var может ухудшить читабельность.Подробнее Microsoft docs состояние:

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

8 голосов
/ 07 сентября 2010

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

В приведенном ниже примере происходит неявное преобразование из object в XmlNode (неуниверсальный интерфейс IEnumerator возвращает только object).Если вы просто замените явное объявление переменной цикла на ключевое слово var, это неявное преобразование больше не будет выполняться:

using System;
using System.Xml;

class Program
{
    static void Foo(object o)
    {
        Console.WriteLine("object overload");
    }

    static void Foo(XmlNode node)
    {
        Console.WriteLine("XmlNode overload");
    }

    static void Main(string[] args)
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml("<root><child/></root>");

        foreach (XmlNode node in doc.DocumentElement.ChildNodes)
        {
            Foo(node);
        }

        foreach (var node in doc.DocumentElement.ChildNodes)
        {
            // oops! node is now of type object!
            Foo(node);
        }
    }
}

В результате этот код на самом деле выдает разные выходные данные в зависимости от того, использовали ли выvar или явный тип.При var будет выполнена перегрузка Foo(object), в противном случае будет перегрузка Foo(XmlNode).Выходные данные вышеприведенной программы, таким образом, таковы:

XmlNode overload
object overload

Обратите внимание, что это поведение полностью соответствует спецификации языка C #.Единственная проблема заключается в том, что var выводит другой тип (object) , чем вы ожидаете , и что этот вывод не очевиден при просмотре кода.

Я не добавилIL, чтобы держать это коротким.Но если вы хотите, вы можете взглянуть на ildasm и убедиться, что компилятор фактически генерирует разные инструкции IL для двух циклов foreach.

7 голосов
/ 07 сентября 2010

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

Одним из них являются подробности реализации анонимных методов, которые могут привести к предупреждению R # «Доступ к измененному замыканию» и поведение, которое совсем не то, что вы могли бы ожидать от просмотра кода.В отличие от var, который может быть объяснен в нескольких предложениях, такое поведение требует трех длинных постов в блоге, которые включают вывод дизассемблера для полного объяснения:

Означает ли это, что вам также не следует использовать анонимные методы (например, делегаты, лямбды) и библиотеки, которые используют их, такие как Linqили ParallelFX только потому, что в определенных странных обстоятельствах поведение может не соответствовать ожидаемому?

Конечно, нет.

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

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

4 голосов
/ 07 сентября 2010

Они утверждают, что использование var "может привести к неожиданным результатам в некоторых случаях". К неожиданным результатам в некоторых случаях ".

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

Ключевое слово var - это функция времени компиляции. Компилятор вставит соответствующий тип для объявления. Вот почему вы не можете делать такие вещи, как:

var my_variable = null
or
var my_variable;

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

4 голосов
/ 07 сентября 2010

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

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

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

1 голос
/ 07 сентября 2010

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

1 голос
/ 08 сентября 2010

var лучше всего использовать, когда у вас явно декларация

ArrayList<Entity> en = new ArrayList<Enity>()

затрудняет читаемость

var en = new ArrayList<Entity>()

Ленивый, понятный код, мне нравится

...