Чи вплине використання "var" на ефективність?


230

Раніше я задавав питання про те, чому я бачу так багато прикладів використання varключового слова, і я отримав відповідь, що хоча це потрібно лише для анонімних типів, він все-таки використовується для того, щоб зробити написання коду "швидшим" ​​/ простішим та "просто тому".

Перейшовши за цим посиланням ("C # 3.0 - Var Is Objec"), я побачив, що varв ІЛ збивається відповідний тип (ви побачите це приблизно в середині статті).

Моє запитання полягає в тому, наскільки більше, якщо такий є, ІЛ-код за допомогою varключового слова займає, і чи було б це навіть близько до того, щоб вимірювати рівень продуктивності коду, якби він використовувався скрізь?


1
на питання, відповів століттями тому, просто хотів додати ще одну річ проти var - незважаючи на те, що вона вирішена під час компіляції, вона не помічена належним чином "Знайти всі посилання" Visual Studio та "Find Usages" Resharper, якщо ви хочете знайти всі типи використання - і це не буде виправлено, бо воно буде надто повільним.
KolA

@KolA Змінні, задекларовані varнайбільш точно, працюють із "Знайти всі посилання" у Visual Studio 2019, тож якщо вона коли-небудь була порушена, її було виправлено. Але я можу підтвердити, що він працює так само, як і Visual Studio 2012, тому я не впевнений, чому ви заявили, що це не працює.
Герохтар

@Herohtar спробуйте наступний код "клас X {} X GetX () {повернути новий X ();} недійсний UseX () {var x = GetX ();}" і знайти всі посилання на X, "var x = GetX ( ) "біт не виділяється - в останньому VS2019 станом на сьогодні це саме те, що я мав на увазі. Це виділяється, хоча якщо ви використовуєте "X x = GetX ()" замість var
KolA

1
@KolA О, я бачу, що ви маєте на увазі - varне буде вважатися посиланням на те, Xколи ви використовуєте "Знайти всі посилання" X. Цікаво, що якщо ви використовуєте «Знайти всі посилання» на varв цій заяві, він буде показувати вам посилання X(хоча він все ще не перерахує varзаяву). Крім того, коли курсор увімкнено var, він виділить усі екземпляри Xв одному документі (і навпаки).
Герохтар

Відповіді:


316

Немає додаткового коду IL для цього var ключового слова : отриманий 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
Гм, що означає ІЛ у C #? Я ніколи не чув про це.
puretppc

15
У вашому прикладі 3-х рядків коду, які поводяться по-різному, перший рядок не складається . Друга і третя рядки, які і роблять узагальнювати, робити те ж саме. Якщо Fooповертається a List, а не an IList, то всі три рядки збиралися б, але третій рядок поводився б як перший рядок , а не другий.
Сервіс

72

Як каже Джоел, компілятор працює в час компіляції, яким повинен бути тип вар, фактично це лише хитрість, яку виконує компілятор, щоб зберегти натискання клавіш, наприклад,

var s = "hi";

замінюється на

string s = "hi";

компілятором до створення будь-якого ІЛ. Згенерований 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);
}

Тож відповідь явно не є хітом виконання часу!


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змінної під час компіляції. Немає різниці в генерованому ІЛ.


14

Отже, щоб було зрозуміло, це лінивий стиль кодування. Я віддаю перевагу рідним типам, враховуючи вибір; Я візьму цей додатковий шматочок "шуму", щоб переконатися, що я пишу та читаю саме те, що, на мою думку, перебуваю під час коду / налагодження. * знизати плечима *


1
Це лише ваш суб'єктивний погляд, а не відповідь на питання про продуктивність. Правильна відповідь - це не впливає на продуктивність. Я проголосував за закриття
Андерс

Це не відповідає на питання, чи varвпливає взагалі на ефективність роботи; ви просто висловлюєте свою думку про те, чи повинні люди її використовувати.
Герохтар

Визначення типу із значення пізніше, наприклад, перехід від int 5 до плаваючого 5.25, може абсолютно спричинити проблеми з продуктивністю. *
знизати

Ні, це не спричинить жодних проблем з продуктивністю; ви отримаєте помилки побудови в будь-яких місцях, які очікували змінної типу, intоскільки вона не може автоматично перетворити float, але це точно те саме, що станеться, якщо ви явно використали intі потім змінили на float. У будь-якому випадку, ваша відповідь все ще не відповідає на питання "чи varвпливає використання на ефективність?" (особливо з точки зору генерованого ІЛ)
Herohtar

8

Я не думаю, що ти правильно зрозумів те, що ти читав. Якщо він компілюється до правильного типу, то немає ніякої різниці. Коли я це роблю:

var i = 42;

Компілятор знає, що це int, і генерує код так, як ніби я написав

int i = 42;

Як йдеться у публікації, з якою ви посилаєтесь, вона збирається до одного типу. Це не перевірка часу виконання чи інше, що вимагає додаткового коду. Компілятор просто з'ясовує, яким повинен бути тип, і використовує це.


Правильно, але що робити, якщо пізніше ви 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);

Якщо ви пишете для студентів, то їжте власну собачу їжу і завжди пишіть її тим же «правильним» способом, послідовно. Студенти часто беруть до душі речі на 100% дослідно, і вони почнуть використовувати будь-які неохайні звички, які вони вибирають по дорозі. $ .02
ChrisH

1

"вар" - одна з тих речей, яку люди або люблять, або ненавидять (наприклад, регіони). Хоча, на відміну від регіонів, var абсолютно необхідний при створенні анонімних класів.

Для мене var має сенс, коли ви створюєте об'єкт безпосередньо, наприклад:

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

Коли це сказано, ви можете просто зробити:

Dictionary<string, string> dict = нове та інтелігенційне заповнить для вас решту тут.

Якщо ви хочете працювати лише з певним інтерфейсом, ви не можете використовувати var, якщо метод, який ви викликаєте, повертає інтерфейс безпосередньо.

Resharper, здається, стоїть на боці використання "var" у всьому світі, що може підштовхнути більше людей до цього. Але я згодна з тим, що важче читати, якщо ви викликаєте метод, і не очевидно, що повертається ім'ям.

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


0

Це залежить від ситуації, якщо ви спробуєте використовувати цей код нижче.

Вираз перетворюється на "OBJECT" і настільки знижує продуктивність, але це окрема проблема.

КОД:

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();
    }

З повагою

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.