¿El uso de 'var' afectará el rendimiento?

230

Anteriormente, hice una pregunta sobre por qué veo tantos ejemplos que usan la varpalabra clave y obtuve la respuesta de que, aunque solo es necesario para los tipos anónimos, se usa para hacer que la escritura de código sea 'más rápida' / más fácil y 'solo porque'.

Siguiendo este enlace ("C # 3.0 - Var no es un objeto") , vi que varse compila en el tipo correcto en el IL (lo verá sobre el artículo a mitad de camino).

Mi pregunta es ¿cuánto más, si es que hay alguno, usa el código IL usando la varpalabra clave, y estaría incluso cerca de tener un nivel medible en el rendimiento del código si se usara en todas partes?

Jeff Keslinke
fuente
1
pregunta respondida hace mucho tiempo, solo quería agregar una cosa más contra var: a pesar de haberse resuelto en el momento de la compilación, no es detectado correctamente por "Buscar todas las referencias" de Visual Studio y "Buscar usos" de Resharper si desea encontrar todos los usos del tipo - y no se solucionará porque sería demasiado lento.
KolA
Las variables de @KolA declaradas vardefinitivamente funcionan con "Buscar todas las referencias" en Visual Studio 2019, por lo que si alguna vez se rompió, se ha solucionado. Pero puedo confirmar que funciona desde Visual Studio 2012, así que no estoy seguro de por qué afirmas que no funcionó.
Herohtar
@Herohtar intente seguir el código "clase X {} X GetX () {return new X ();} void UseX () {var x = GetX ();}" y Buscar todas las referencias a X, el "var x = GetX ( ) "el bit no está resaltado - en el último VS2019 a partir de ahora, esto es lo que quise decir. Sin embargo, se resalta si usa "X x = GetX ()" en lugar de var
KolA
1
@KolA Ah, entiendo lo que quieres decir: varno se considerará una referencia Xcuando uses "Buscar todas las referencias" en X. Curiosamente, si usa "Buscar todas las referencias" varen esa declaración, le mostrará referencias a X(aunque todavía no incluirá la vardeclaración). Además, cuando el cursor está activado var, resaltará todas las instancias del Xmismo documento (y viceversa).
Herohtar

Respuestas:

316

No hay un código IL adicional para la varpalabra clave: la IL resultante debe ser idéntica para los tipos no anónimos. Si el compilador no puede crear esa IL porque no puede determinar qué tipo de intención tenía de usar, obtendrá un error del compilador.

El único truco es que varinferirá un tipo exacto en el que puede haber elegido una interfaz o un tipo principal si tuviera que configurar el tipo manualmente.


Actualización 8 años después

Necesito actualizar esto ya que mi comprensión ha cambiado. Ahora creo que es posible varafectar el rendimiento en la situación en la que un método devuelve una interfaz, pero habría utilizado un tipo exacto. Por ejemplo, si tiene este método:

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

Considere estas tres líneas de código para llamar al método:

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

Los tres compilan y ejecutan como se esperaba. Sin embargo, las dos primeras líneas no son exactamente iguales, y la tercera línea coincidirá con la segunda, en lugar de la primera. Porque la firma de Foo()es devolver unIList<int> , así es como el compilador construirá la bar3variable.

Desde el punto de vista del rendimiento, en su mayoría no lo notará. Sin embargo, hay situaciones en las que el rendimiento de la tercera línea puede no ser tan rápido como el rendimiento de la primera . A medida que continúe usando elbar3 variable, es posible que el compilador no pueda enviar llamadas a métodos de la misma manera.

Tenga en cuenta que es posible (incluso probable) que el jitter pueda borrar esta diferencia, pero no está garantizado. En general, aún debe considerar varque no es un factor en términos de rendimiento. Ciertamente no es como usar una dynamicvariable. Pero decir que nunca hace la diferencia puede ser exagerado.

Joel Coehoorn
fuente
23
La IL no solo debe ser idéntica, es idéntica. var i = 42; compila exactamente al mismo código que int i = 42;
Brian Rasmussen
15
@BrianRasmussen: Sé que tu publicación es antigua, pero supongo que var i = 42;(el tipo de infers es int) NO es idéntico a long i = 42;. Entonces, en algunos casos, puede estar haciendo suposiciones incorrectas sobre la inferencia de tipos. Esto podría causar errores de tiempo de ejecución delusive / edge case si el valor no se ajusta. Por esa razón, aún puede ser una buena idea ser explícito cuando el valor no tiene un tipo explícito. Entonces, por ejemplo, var x = new List<List<Dictionary<int, string>()>()>()sería aceptable, pero var x = 42es algo ambiguo y debe escribirse como int x = 42. Pero a cada uno lo suyo ...
Nelson Rothermel
50
@NelsonRothermel: var x = 42; no es ambiguo. Los literales enteros son del tipo int. Si quieres un largo literal, escribe var x = 42L;.
Brian Rasmussen
66
¿Qué significa IL en C #? Realmente nunca escuché de eso.
puretppc
15
En su ejemplo de las 3 líneas de código que se comportan de manera diferente, la primera línea no se compila . La segunda y tercera líneas, que tanto hacen compilación, hacen exactamente lo mismo. Si se Foodevuelve a List, en lugar de an IList, las tres líneas se compilarían, pero la tercera línea se comportaría como la primera línea , no como la segunda.
Servy
72

Como dice Joel, el compilador calcula en tiempo de compilación qué tipo de var debería ser, efectivamente, es solo un truco que realiza el compilador para guardar las pulsaciones de teclas, por ejemplo

var s = "hi";

es reemplazado por

string s = "hi";

por el compilador antes de que se genere cualquier IL. La IL generada será exactamente la misma que si hubiera escrito una cadena.

ljs
fuente
26

Como nadie ha mencionado el reflector todavía ...

Si compila el siguiente código C #:

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

Luego use reflector en él, obtendrá:

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

¡Entonces la respuesta es claramente que no hay éxito en el tiempo de ejecución!

RichardOD
fuente
17

Para el siguiente método:

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

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

La salida IL es esta:

        {
          .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
Robar
fuente
14

El compilador de C # infiere el tipo verdadero de la varvariable en tiempo de compilación. No hay diferencia en la IL generada.

Michael Burr
fuente
14

Entonces, para ser claros, es un estilo de codificación perezosa. Prefiero los tipos nativos, dada la opción; Tomaré ese poco de "ruido" adicional para asegurarme de que estoy escribiendo y leyendo exactamente lo que creo que estoy en el momento del código / depuración. * encogerse de hombros *

ChrisH
fuente
1
Esa es solo su visión subjetiva y no una respuesta a la pregunta sobre el rendimiento. La respuesta correcta es que no tiene impacto en el rendimiento. He votado a favor
Anders, el
Esto no responde a la pregunta de si varafecta el rendimiento en absoluto; solo estás expresando tu opinión sobre si las personas deberían usarlo.
Herohtar
Inferir el tipo del valor más adelante, por ejemplo, cambiar de int 5 a float 5.25, puede causar problemas de rendimiento. * encogiéndose de hombros *
ChrisH
No, eso no causará problemas de rendimiento; obtendrá errores de compilación en cualquier lugar que esperaba una variable de tipo intporque no puede convertir automáticamente float, pero eso es exactamente lo mismo que sucedería si lo usara explícitamente inty luego lo cambiara float. En cualquier caso, su respuesta aún no responde a la pregunta "¿el uso varafecta el rendimiento?" (particularmente en términos de IL generada)
Herohtar
8

No creo que hayas entendido bien lo que lees. Si se compila con el tipo correcto, entonces no es ninguna diferencia. Cuando hago esto:

var i = 42;

El compilador sabe que es un int y genera código como si hubiera escrito

int i = 42;

Como dice la publicación a la que se vinculó, se compila al mismo tipo. No es una verificación de tiempo de ejecución ni ninguna otra cosa que requiera código adicional. El compilador solo descubre cuál debe ser el tipo y lo usa.

jalf
fuente
Correcto, pero ¿y si luego i = i - someVar y someVar = 3.3. Soy un Int, ahora. Es mejor ser explícito no solo para darle al compilador una ventaja inicial en la búsqueda de fallas, sino también para minimizar los errores de tiempo de ejecución o las conversiones de tipo que ralentizan el proceso. * encogimiento de hombros * También mejora el código para la autodescripción. He estado haciendo esto mucho, mucho tiempo. Tomaré el código "ruidoso" con tipos explícitos cada vez, dada la opción.
ChrisH
5

No hay costo de rendimiento de tiempo de ejecución por usar var. Sin embargo, sospecharía que hay un costo de rendimiento de compilación ya que el compilador necesita inferir el tipo, aunque probablemente sea insignificante.

Brian Rudolph
fuente
10
El RHS tiene que tener su tipo calculado de todos modos: el compilador detectaría los tipos no coincidentes y arrojaría un error, así que creo que no es realmente un costo.
Jimmy
3

Si el compilador puede hacer inferencias de tipo automáticas, entonces no habrá ningún problema con el rendimiento. Ambos generarán el mismo código

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

sin embargo, si está construyendo el tipo dinámicamente (LINQ ...), entonces vares su única pregunta y hay otro mecanismo con el que comparar para decir cuál es la penalización.

azulado
fuente
3

Siempre uso la palabra var en artículos web o escritos de guías.

El ancho del editor de texto del artículo en línea es pequeño.

Si escribo esto:

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

Verá que el texto del código anterior representado anteriormente es demasiado largo y sale de la caja, se oculta. El lector debe desplazarse hacia la derecha para ver la sintaxis completa.

Es por eso que siempre uso la palabra clave var en los escritos de artículos web.

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

Todo el pre código prestado solo cabe dentro de la pantalla.

En la práctica, para declarar objetos, rara vez uso var, confío en intellisense para declarar objetos más rápido.

Ejemplo:

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

Pero, para devolver el objeto de un método, uso var para escribir código más rápido.

Ejemplo:

var coolObject = GetCoolObject(param1, param2);
mjb
fuente
Si estás escribiendo para estudiantes, entonces come tu propia comida para perros y siempre escríbela de la misma manera "correcta", de manera consistente. Los estudiantes a menudo toman las cosas 100% literalmente y en serio, y comenzarán a usar cualquier hábito descuidado que tomen en el camino. $ .02
ChrisH
1

"var" es una de esas cosas que la gente ama u odia (como las regiones). Sin embargo, a diferencia de las regiones, var es absolutamente necesario al crear clases anónimas.

Para mí, var tiene sentido cuando estás renovando un objeto directamente como:

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

Dicho esto, puedes hacer fácilmente:

Dictionary<string, string> dict = nuevo e intellisense completará el resto aquí.

Si solo desea trabajar con una interfaz específica, no puede usar var a menos que el método al que llama devuelva la interfaz directamente.

Resharper parece estar del lado de usar "var" en todas partes, lo que puede empujar a más personas a hacerlo de esa manera. Pero estoy de acuerdo en que es más difícil de leer si está llamando a un método y no es obvio lo que devuelve el nombre.

var en sí no ralentiza las cosas, pero hay una advertencia sobre esto que no mucha gente piensa. Si lo hace var result = SomeMethod();, el código después de eso espera algún tipo de resultado donde llamaría a varios métodos o propiedades o lo que sea. Si SomeMethod()cambió su definición a otro tipo pero aún cumplió con el contrato que esperaba el otro código, acaba de crear un error realmente desagradable (por supuesto, si no hay pruebas de unidad / integración).

Daniel Lorenz
fuente
0

Depende de la situación, si intentas usar este código a continuación.

La expresión se convierte en "OBJETO" y disminuye mucho el rendimiento, pero es un problema aislado.

CÓDIGO:

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

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

Si desea ejecutar este código, use el siguiente código y obtenga la diferencia de veces.

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

Saludos

Silvio Garcez
fuente