L'uso di "var" influirà sulle prestazioni?


230

In precedenza ho fatto una domanda sul perché vedo così tanti esempi usare la varparola chiave e ho ottenuto la risposta che mentre è necessario solo per i tipi anonimi, che viene comunque utilizzato per rendere la scrittura del codice "più veloce" / più facile e "solo perché".

Seguendo questo link ("C # 3.0 - Var Isn't Objec") ho visto che varviene compilato fino al tipo corretto nell'IL (lo vedrai a metà articolo).

La mia domanda è: quanto più, eventualmente, il codice IL utilizza la varparola chiave, e sarebbe persino vicino ad avere un livello misurabile sulle prestazioni del codice se fosse usato ovunque?


1
domanda con risposta anni fa, volevo solo aggiungere un'altra cosa contro var - nonostante sia stato risolto in fase di compilazione, non viene individuato correttamente da "Trova tutti i riferimenti" di Visual Studio e "Trova usi" di Resharper se si desidera trovare tutti gli usi del tipo - e non verrà risolto perché sarebbe troppo lento.
KolA,

@KolA Le variabili dichiarate varfunzionano sicuramente con "Trova tutti i riferimenti" in Visual Studio 2019, quindi se è mai stato rotto è stato corretto. Ma posso confermare che funziona fino a Visual Studio 2012, quindi non sono sicuro del motivo per cui hai affermato che non funzionava.
Herohtar,

@Herohtar prova a seguire il codice "class X {} X GetX () {return new X ();} void UseX () {var x = GetX ();}" e trova tutti i riferimenti a X, il "var x = GetX ( ) "il bit non è evidenziato - nell'ultimo VS2019 a partire da ora, questo è ciò che intendevo. È evidenziato però se si utilizza "X x = GetX ()" invece di var
KolA

1
@KolA Ah, capisco cosa intendi: varnon verrà considerato un riferimento a Xquando usi "Trova tutti i riferimenti" su X. È interessante notare che se si utilizza "Trova tutti i riferimenti" varin quella dichiarazione, ti mostrerà i riferimenti X(anche se non elencherà ancora la vardichiarazione). Inoltre, quando il cursore è attivo var, evidenzierà tutte le istanze Xnello stesso documento (e viceversa).
Herohtar,

Risposte:


316

Non esiste un codice IL aggiuntivo per la varparola chiave: l'IL risultante dovrebbe essere identico per i tipi non anonimi. Se il compilatore non è in grado di creare quell'IL perché non riesce a capire quale tipo si intendesse utilizzare, verrà visualizzato un errore del compilatore.

L'unico trucco è che varinferirà un tipo esatto in cui potresti aver scelto un'interfaccia o un tipo genitore se dovessi impostare il tipo manualmente.


Aggiornamento 8 anni dopo

Devo aggiornarlo perché la mia comprensione è cambiata. Ora credo che possa essere possibile varinfluire sulle prestazioni nella situazione in cui un metodo restituisce un'interfaccia, ma avresti usato un tipo esatto. Ad esempio, se si dispone di questo metodo:

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

Considera queste tre righe di codice per chiamare il metodo:

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

Tutti e tre vengono compilati ed eseguiti come previsto. Tuttavia, le prime due righe non sono esattamente uguali e la terza corrisponderà alla seconda anziché alla prima. Poiché la firma di Foo()deve restituire un IList<int>, è così che il compilatore costruirà la bar3variabile.

Dal punto di vista delle prestazioni, per lo più non lo noterai. Tuttavia, ci sono situazioni in cui le prestazioni della terza linea potrebbero non essere così veloci come le prestazioni della prima . Mentre si continua a utilizzare la bar3variabile, il compilatore potrebbe non essere in grado di inviare chiamate di metodo allo stesso modo.

Nota che è possibile (probabilmente anche) il jitter sarà in grado di cancellare questa differenza, ma non è garantito. In generale, dovresti comunque considerare vardi non essere un fattore in termini di prestazioni. Certamente non è affatto come usare una dynamicvariabile. Ma dire che non fa mai alcuna differenza potrebbe essere sopravvalutarlo.


23
Non solo l'IL dovrebbe essere identico, ma è identico. var i = 42; compila esattamente con lo stesso codice di int i = 42;
Brian Rasmussen,

15
@BrianRasmussen: So che il tuo post è vecchio è vecchio, ma presumo var i = 42;(tipo infers è int) NON identico a long i = 42;. Quindi in alcuni casi potresti fare ipotesi errate sull'inferenza del tipo. Ciò potrebbe causare errori di runtime inafferrabili / limite se il valore non si adatta. Per tale motivo, può essere comunque una buona idea essere espliciti quando il valore non ha un tipo esplicito. Quindi, per esempio, var x = new List<List<Dictionary<int, string>()>()>()sarebbe accettabile, ma var x = 42è in qualche modo ambiguo e dovrebbe essere scritto come int x = 42. Ma a ciascuno il proprio ...
Nelson Rothermel,

50
@NelsonRothermel: var x = 42; non è ambiguo. I letterali interi sono del tipo int. Se vuoi un lungo letterale scrivi var x = 42L;.
Brian Rasmussen,

6
Cosa significa IL in C #? Non ne ho mai veramente sentito parlare.
Puretppc,

15
Nel tuo esempio delle 3 righe di codice che si comportano in modo diverso, la prima riga non viene compilata . La seconda e la terza linea, che entrambi fanno di compilazione, fanno esattamente la stessa cosa. Se Foorestituito a List, anziché an IList, tutte e tre le righe verrebbero compilate ma la terza riga si comporterebbe come la prima , non la seconda.
Servito il

72

Come dice Joel, il compilatore risolve in fase di compilazione quale tipo var dovrebbe essere, in effetti è solo un trucco che il compilatore esegue per salvare i tasti premuti, quindi ad esempio

var s = "hi";

viene sostituito da

string s = "hi";

dal compilatore prima che venga generato un IL. IL generato sarà esattamente lo stesso di se avessi digitato stringa.


26

Dato che nessuno ha ancora menzionato il riflettore ...

Se si compila il seguente codice C #:

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

Quindi usa il riflettore su di esso, ottieni:

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

Quindi la risposta è chiaramente nessuna hit di runtime!


17

Per il seguente metodo:

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

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

L'output IL è questo:

        {
          .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

Il compilatore C # deduce il vero tipo della varvariabile al momento della compilazione. Non c'è differenza nell'IL generato.


14

Quindi, per essere chiari, è uno stile di codifica pigro. Preferisco i tipi nativi, data la scelta; Prenderò quel po 'di "rumore" in più per assicurarmi di scrivere e leggere esattamente quello che penso di essere al momento del codice / debug. * scrollata di spalle *


1
Questa è solo la tua visione soggettiva e non una risposta alla domanda sulla performance. La risposta giusta è che non ha alcun impatto sulle prestazioni. Ho votato a favore
Anders

Questo non risponde alla domanda se varinfluisce sulle prestazioni; stai solo dichiarando la tua opinione sull'opportunità o meno di usarla.
Herohtar,

Il trasferimento del tipo dal valore in un secondo momento, ad esempio, il passaggio da int 5 a float 5.25, può assolutamente causare problemi di prestazioni. * scrollata di spalle *
ChrisH,

No, ciò non causerà problemi di prestazioni; otterrai errori di compilazione in tutti i luoghi in cui si aspettava una variabile di tipo intperché non è in grado di convertirlo automaticamente float, ma è esattamente la stessa cosa che succederebbe se tu lo utilizzassi esplicitamente inte poi cambiassi float. In ogni caso, la tua risposta non risponde ancora alla domanda "l'utilizzo delle varprestazioni influisce?" (in particolare in termini di IL generato)
Herohtar il

8

Non penso che tu abbia capito bene cosa leggi. Se viene compilato al tipo corretto, allora non v'è alcuna differenza. Quando faccio questo:

var i = 42;

Il compilatore sa che è un int e genera codice come se avessi scritto

int i = 42;

Come dice il post a cui ti sei collegato, viene compilato nello stesso tipo. Non è un controllo di runtime o qualsiasi altra cosa che richiede un codice aggiuntivo. Il compilatore capisce solo quale deve essere il tipo e lo usa.


Giusto, ma cosa succede se dopo i = i - someVar e someVar = 3.3. sono un Int, ora. È meglio essere espliciti non solo per dare al compilatore un vantaggio sulla ricerca di difetti, ma anche per ridurre al minimo gli errori di runtime o le conversioni di tipo che rallentano il processo. * scrollata di spalle * Rende anche il codice migliore per auto-descriversi. Lo sto facendo da molto, molto tempo. Prenderò il codice "rumoroso" con tipi espliciti ogni volta, data la scelta.
ChrisH

5

Non ci sono costi di runtime nell'uso di var. Tuttavia, sospetterei che ci sia un costo per la compilazione poiché il compilatore deve dedurre il tipo, anche se molto probabilmente sarà trascurabile.


10
RHS deve comunque calcolare il suo tipo: il compilatore rileverà i tipi non corrispondenti e genererebbe un errore, quindi non è un costo, credo.
Jimmy,

3

Se il compilatore può eseguire inferenze di tipo automatiche, non ci saranno problemi con le prestazioni. Entrambi genereranno lo stesso codice

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

tuttavia, se stai costruendo il tipo in modo dinamico (LINQ ...), allora varè la tua unica domanda e c'è un altro meccanismo da confrontare per dire qual è la penalità.


3

Uso sempre la parola var negli articoli web o negli scritti delle guide.

La larghezza dell'editor di testo dell'articolo online è piccola.

Se scrivo questo:

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

Vedrai che il testo del pre-codice sopra visualizzato è troppo lungo e scorre fuori dalla scatola, viene nascosto. Il lettore deve scorrere verso destra per visualizzare la sintassi completa.

Ecco perché uso sempre la parola chiave var negli scritti di articoli web.

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

L'intero pre codice reso si adatta allo schermo.

In pratica, per dichiarare l'oggetto, uso raramente var, mi affido a intellisense per dichiarare l'oggetto più velocemente.

Esempio:

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

Ma, per restituire l'oggetto da un metodo, uso var per scrivere il codice più velocemente.

Esempio:

var coolObject = GetCoolObject(param1, param2);

Se stai scrivendo per gli studenti, mangia il tuo cibo per cani e scrivilo sempre nello stesso modo "corretto", in modo coerente. Gli studenti prendono spesso le cose al 100% alla lettera e a cuore, e inizieranno a usare qualsiasi abitudine sciatta che prendono lungo la strada. $ 0,02
ChrisH,

1

"var" è una di quelle cose che le persone amano o odiano (come le regioni). Tuttavia, a differenza delle regioni, var è assolutamente necessario quando si creano classi anonime.

Per me, var ha senso quando si sta rinnovando un oggetto direttamente come:

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

Detto questo, puoi facilmente fare:

Dictionary<string, string> dict = nuovo e intellisense riempiranno il resto per te qui.

Se vuoi lavorare solo con un'interfaccia specifica, non puoi usare var a meno che il metodo che stai chiamando restituisca direttamente l'interfaccia.

Resharper sembra essere dalla parte dell'uso di "var" dappertutto, il che potrebbe spingere più persone a farlo in quel modo. Ma sono d'accordo sul fatto che è più difficile leggere se si sta chiamando un metodo e non è ovvio cosa viene restituito dal nome.

var stesso non rallenta le cose, ma c'è un avvertimento a cui non molte persone pensano. Se lo fai, var result = SomeMethod();il codice dopo si aspetta una sorta di risultato indietro nel quale chiameresti vari metodi o proprietà o altro. Se ha SomeMethod()cambiato la sua definizione in un altro tipo ma ha comunque soddisfatto il contratto previsto dall'altro codice, hai appena creato un bug davvero sgradevole (se ovviamente nessun test di unità / integrazione).


0

Dipende dalla situazione, se si tenta di utilizzare questo codice sotto.

L'espressione viene convertita in "OGGETTO" e diminuisce così tanto le prestazioni, ma è un problema isolato.

CODICE:

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

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

Se si desidera eseguire questo codice, utilizzare il codice qui sotto e ottenere la differenza di volte.

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

Saluti

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.