Повлияет ли использование var на производительность?

230

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

По этой ссылке («C # 3.0 - Var Is Not Objec») я увидел, что он varскомпилирован в правильный тип в IL (вы увидите его в середине статьи).

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

Джефф Кеслинке
источник
1
На вопрос, на который давным-давно ответили, я просто хотел добавить еще одну вещь к var - несмотря на то, что он был решен во время компиляции, он не обнаружен должным образом в «Найти все ссылки» в Visual Studio и в «Найти использование» в Resharper, если вы хотите найти все использования типа - и это не будет исправлено, потому что это будет слишком медленно.
КолА
@KolA Переменные, объявленные varнаиболее точно, работают с «Find All References» в Visual Studio 2019, поэтому, если он когда-либо был сломан, он был исправлен. Но я могу подтвердить, что он работает еще в Visual Studio 2012, поэтому я не уверен, почему вы утверждали, что он не работает.
Herohtar
@Herohtar попробуйте следующий код "class X {} X GetX () {return new X ();} void UseX () {var x = GetX ();}" и найдите все ссылки на X, "var x = GetX ( ) "немного не выделено - в последней VS2019 на данный момент это то, что я имел ввиду. Это будет выделено, хотя, если вы используете «X x = GetX ()» вместо var
KolA
1
@KolA Ах, я понимаю, что вы имеете в виду - varне будет считаться ссылкой, Xкогда вы используете "Найти все ссылки" на X. Интересно, что если вы используете «Найти все ссылки» на varв этом заявлении, он будет показывать вам ссылки X(хотя он все еще не перечислит varзаявление). Кроме того, когда курсор включен var, он будет выделять все экземпляры Xв одном и том же документе (и наоборот).
Herohtar

Ответы:

316

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

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


Обновление 8 лет спустя

Мне нужно обновить это, так как мое понимание изменилось. Теперь я считаю, что это может varповлиять на производительность в ситуации, когда метод возвращает интерфейс, но вы бы использовали точный тип. Например, если у вас есть этот метод:

IList<int> Foo()
{
    return Enumerable.Range(0,10).ToList();
}

Рассмотрим эти три строки кода для вызова метода:

List<int> bar1 = Foo();
IList<int> bar = Foo();
var bar3 = Foo();

Все три компилируются и выполняются, как и ожидалось. Однако первые две строки не совсем совпадают, а третья строка будет соответствовать второй, а не первой. Поскольку подпись Foo()должна возвращать IList<int>, именно так компилятор будет создавать bar3переменную.

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

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

Джоэл Коухорн
источник
23
Необходимо не только IL быть идентичны - это является идентичным. var i = 42; компилируется в тот же код, что и int i = 42;
Брайан Расмуссен
15
@BrianRasmussen: я знаю, что ваш пост старый, но я предполагаю var i = 42;(вывод типа int) НЕ идентичен long i = 42;. Так что в некоторых случаях вы можете делать неверные предположения о выводе типа. Это может вызвать неуловимые / крайние ошибки времени выполнения, если значение не подходит. По этой причине, все еще может быть хорошей идеей быть явным, если значение не имеет явного типа. Так, например, var x = new List<List<Dictionary<int, string>()>()>()было бы приемлемо, но var x = 42несколько двусмысленно и должно быть записано как int x = 42. Но каждому свое ...
Нельсон Ротермел
50
@NelsonRothermel: var x = 42; не однозначно. Целочисленные литералы имеют тип int. Если хочешь буквально долго пиши var x = 42L;.
Брайан Расмуссен
6
Хм, что означает IL в C #? Я никогда не слышал об этом.
puretppc
15
В вашем примере из 3 строк кода, которые ведут себя по-разному, первая строка не компилируется . Вторая и третья строки, которые и делают обобщать, делать то же самое. Если Fooвозвращается a List, а не an IList, тогда все три строки будут компилироваться, но третья строка будет вести себя как первая строка , а не вторая.
Servy
72

Как говорит Джоэл, компилятор работает при компиляции , что должно быть тип вар, эффективно это просто трюк выполняет компилятор , чтобы сохранить нажатия клавиш, так, например ,

var s = "hi";

заменяется

string s = "hi";

компилятором до того, как будет сгенерирован любой IL. Сгенерированный IL будет точно таким же, как если бы вы набрали строку.

СМЛ
источник
26

Как никто еще не упомянул отражатель ...

Если вы компилируете следующий код C #:

static void Main(string[] args)
{
    var x = "hello";
    string y = "hello again!";
    Console.WriteLine(x);
    Console.WriteLine(y);
}

Затем, используя отражатель, вы получите:

// Methods
private static void Main(string[] args)
{
    string x = "hello";
    string y = "hello again!";
    Console.WriteLine(x);
    Console.WriteLine(y);
}

Таким образом, ответ явно не влияет на производительность во время выполнения!

RichardOD
источник
17

Для следующего метода:

   private static void StringVsVarILOutput()
    {
        var string1 = new String(new char[9]);

        string string2 = new String(new char[9]);
    }

Выход IL это:

        {
          .method private hidebysig static void  StringVsVarILOutput() cil managed
          // Code size       28 (0x1c)
          .maxstack  2
          .locals init ([0] string string1,
                   [1] string string2)
          IL_0000:  nop
          IL_0001:  ldc.i4.s   9
          IL_0003:  newarr     [mscorlib]System.Char
          IL_0008:  newobj     instance void [mscorlib]System.String::.ctor(char[])
          IL_000d:  stloc.0
          IL_000e:  ldc.i4.s   9
          IL_0010:  newarr     [mscorlib]System.Char
          IL_0015:  newobj     instance void [mscorlib]System.String::.ctor(char[])
          IL_001a:  stloc.1
          IL_001b:  ret
        } // end of method Program::StringVsVarILOutput
обкрадывать
источник
14

Компилятор C # определяет истинный тип varпеременной во время компиляции. Там нет разницы в сгенерированном IL.

Майкл Берр
источник
14

Итак, чтобы быть ясным, это ленивый стиль кодирования. Я предпочитаю нативные типы, учитывая выбор; Я возьму этот лишний «шум», чтобы убедиться, что я пишу и читаю именно то, что, как мне кажется, находится во время кода / отладки. * пожимает плечами *

ChrisH
источник
1
Это только ваш субъективный взгляд, а не ответ на вопрос о производительности. Правильный ответ - это не влияет на производительность. Я голосовал за близких
Андерс
Это не отвечает на вопрос, varвлияет ли это вообще на производительность; Вы просто высказываете свое мнение о том, должны ли люди его использовать.
Herohtar
Например, вывод типа из значения позже, переключение с int 5 на float 5.25, может привести к проблемам с производительностью. *
пожимает
Нет, это не вызовет проблем с производительностью; вы будете получать ошибки сборки в тех местах, где ожидалась переменная типа, intпотому что она не может автоматически преобразовать float, но это точно то же самое, что произошло бы, если вы явно использовали intи затем изменили на float. В любом случае, ваш ответ по-прежнему не отвечает на вопрос " varвлияет ли использование на производительность?" (особенно с точки зрения генерируемого IL)
Herohtar
8

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

var i = 42;

Компилятор знает, что это int, и генерирует код, как если бы я написал

int i = 42;

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

jalf
источник
Правильно, но что если позже вы i = i - someVar и someVar = 3.3. я сейчас Инт Лучше быть явным не только для того, чтобы дать компилятору преимущество при поиске недостатков, но и для минимизации ошибок во время выполнения или замедляющих процесс преобразований типов. * пожимает плечами * Это также делает код лучше для самоописания. Я делал это очень долго. Я буду брать «зашумленный» код с явными типами каждый раз, когда есть возможность выбора.
ChrisH
5

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

Брайан Рудольф
источник
10
в любом случае RHS должен рассчитывать свой тип - компилятор будет отлавливать несовпадающие типы и выдавать ошибку, так что я думаю, что это не слишком дорого.
Джимми
3

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

var    x = new ClassA();
ClassA x = new ClassA();

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

голубоватый
источник
3

Я всегда использую слово var в веб-статьях или руководствах.

Ширина текстового редактора онлайн-статьи невелика.

Если я напишу это:

SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

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

Вот почему я всегда использую ключевое слово var в записях веб-статей.

var coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

Весь представленный предварительный код просто помещается на экране.

На практике для объявления объекта я редко использую var, я полагаюсь на intellisense для более быстрого объявления объекта.

Пример:

SomeCoolNamespace.SomeCoolObject coolObject = new SomeCoolNamespace.SomeCoolObject();

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

Пример:

var coolObject = GetCoolObject(param1, param2);
MJB
источник
Если вы пишете для студентов, то ешьте свою собачью еду и всегда пишите ее одинаково «правильно», последовательно. Студенты часто воспринимают вещи на 100% дословно и начинают использовать любые небрежные привычки, которые они приобретают по пути. $ .02
ChrisH
1

«var» - это одна из тех вещей, которые люди либо любят, либо ненавидят (например, регионы). Хотя, в отличие от регионов, var абсолютно необходим при создании анонимных классов.

Для меня var имеет смысл, когда вы обновляете объект, например:

var dict = new Dictionary<string, string>();

При этом, вы можете легко сделать:

Dictionary<string, string> dict = new и intellisense заменит вам все остальное здесь.

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

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

Сам по себе var не тормозит, но есть одна оговорка, о которой не многие думают. Если вы это сделаете, var result = SomeMethod();то код после этого ожидает некоторого результата назад, где вы будете вызывать различные методы или свойства или что-то еще. Если вы SomeMethod()изменили свое определение на какой-то другой тип, но все равно выполнили контракт, которого ожидал другой код, вы просто создали действительно неприятную ошибку (если, конечно, нет модульных / интеграционных тестов).

Даниэль Лоренц
источник
0

Это зависит от ситуации, если вы попробуете использовать этот код ниже.

Выражение преобразуется в «ОБЪЕКТ» и сильно снижает производительность, но это отдельная проблема.

КОД:

public class Fruta
{
    dynamic _instance;

    public Fruta(dynamic obj)
    {
        _instance = obj;
    }

    public dynamic GetInstance()
    {
        return _instance;
    }
}

public class Manga
{
    public int MyProperty { get; set; }
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
    public int MyProperty3 { get; set; }
}

public class Pera
{
    public int MyProperty { get; set; }
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
}

public class Executa
{
    public string Exec(int count, int value)
    {
        int x = 0;
        Random random = new Random();
        Stopwatch time = new Stopwatch();
        time.Start();

        while (x < count)
        {
            if (value == 0)
            {
                var obj = new Pera();
            }
            else if (value == 1)
            {
                Pera obj = new Pera();
            }
            else if (value == 2)
            {
                var obj = new Banana();
            }
            else if (value == 3)
            {
                var obj = (0 == random.Next(0, 1) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance());
            }
            else
            {
                Banana obj = new Banana();
            }

            x++;
        }

        time.Stop();
        return time.Elapsed.ToString();
    }

    public void ExecManga()
    {
        var obj = new Fruta(new Manga()).GetInstance();
        Manga obj2 = obj;
    }

    public void ExecPera()
    {
        var obj = new Fruta(new Pera()).GetInstance();
        Pera obj2 = obj;
    }
}

Выше результатов с ILSPY.

public string Exec(int count, int value)
{
    int x = 0;
    Random random = new Random();
    Stopwatch time = new Stopwatch();
    time.Start();

    for (; x < count; x++)
    {
        switch (value)
        {
            case 0:
                {
                    Pera obj5 = new Pera();
                    break;
                }
            case 1:
                {
                    Pera obj4 = new Pera();
                    break;
                }
            case 2:
                {
                    Banana obj3 = default(Banana);
                    break;
                }
            case 3:
                {
                    object obj2 = (random.Next(0, 1) == 0) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance();
                    break;
                }
            default:
                {
                    Banana obj = default(Banana);
                    break;
                }
        }
    }
time.Stop();
return time.Elapsed.ToString();
}

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

        static void Main(string[] args)
    {
        Executa exec = new Executa();            
        int x = 0;
        int times = 4;
        int count = 100000000;
        int[] intanceType = new int[4] { 0, 1, 2, 3 };

        while(x < times)
        {                
            Parallel.For(0, intanceType.Length, (i) => {
                Console.WriteLine($"Tentativa:{x} Tipo de Instancia: {intanceType[i]} Tempo Execução: {exec.Exec(count, intanceType[i])}");
            });
            x++;
        }

        Console.ReadLine();
    }

С уважением

Сильвио Гарсес
источник