O uso de 'var' afetará o desempenho?

230

Anteriormente, fiz uma pergunta sobre por que vejo tantos exemplos usando a varpalavra - chave e obtive a resposta de que, embora seja necessário apenas para tipos anônimos, ela é usada para tornar o código de escrita 'mais rápido' / mais fácil e 'apenas porque'.

Seguindo este link ("C # 3.0 - Var Isn't Objec") , vi que ele varé compilado no tipo correto na IL (você verá este artigo no meio do caminho).

Minha pergunta é quanto mais, se é que existe algum, código IL usando a varpalavra - chave, e seria mesmo perto de ter um nível mensurável no desempenho do código se ele fosse usado em qualquer lugar?

Jeff Keslinke
fonte
1
pergunta respondida há alguns anos, só queria adicionar mais uma coisa contra o var - apesar de ter sido resolvido no momento da compilação, ele não é detectado corretamente pelas opções "Localizar todas as referências" do Visual Studio e "Encontrar usos" do Resharper, se você deseja encontrar todos os usos do tipo - e não será corrigido, porque seria muito lento.
KolA 2/11
As variáveis ​​@KolA declaradas com varmais definitivamente trabalham com "Localizar todas as referências" no Visual Studio 2019, portanto, se alguma vez foi quebrado, foi corrigido. Mas posso confirmar que ele funciona desde o Visual Studio 2012, então não sei por que você afirmou que não funcionou.
Herohtar 30/11/19
@Herohtar tente seguir o código "classe X {} X GetX () {retorne novo X ();} cancele UseX () {var x = GetX ();}" e encontre todas as referências a X, o "var x = GetX ( ) "não está destacado - no mais recente VS2019 a partir de agora, foi isso que eu quis dizer. É destacado, porém, se você usar "X x = GetX ()" em vez de var
KolA
1
@KolA Ah, entendo o que você quer dizer - varnão será considerado uma referência Xquando você usar "Localizar todas as referências" X. Curiosamente, se você usar "Localizar todas as referências" na varnessa declaração, ele irá mostrar-lhe as referências a X(embora ele ainda não vai listar a vardeclaração). Além disso, quando o cursor estiver ativado var, ele destacará todas as instâncias do Xmesmo documento (e vice-versa).
Herohtar 5/12/19

Respostas:

316

Não há código de IL extra para a varpalavra-chave: o IL resultante deve ser idêntico para tipos não anônimos. Se o compilador não puder criar essa IL porque não consegue descobrir qual tipo você pretendia usar, você receberá um erro do compilador.

O único truque é varinferir um tipo exato em que você pode ter escolhido um tipo de interface ou pai, se definir o tipo manualmente.


Atualização 8 anos depois

Preciso atualizar isso, pois meu entendimento mudou. Agora acredito que pode ser possível varafetar o desempenho na situação em que um método retorna uma interface, mas você usaria um tipo exato. Por exemplo, se você tiver este método:

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

Considere estas três linhas de código para chamar o método:

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

Todos os três compilam e executam conforme o esperado. No entanto, as duas primeiras linhas não são exatamente iguais, e a terceira linha corresponderá à segunda, e não à primeira. Como a assinatura de Foo()é retornar um IList<int>, é assim que o compilador criará a bar3variável.

Do ponto de vista do desempenho, principalmente você não notará. No entanto, há situações em que o desempenho da terceira linha pode não ser tão rápido quanto o desempenho da primeira . À medida que você continua a usar a bar3variável, o compilador pode não conseguir despachar as chamadas de método da mesma maneira.

Observe que é possível (provavelmente até) o jitter será capaz de apagar essa diferença, mas isso não é garantido. Geralmente, você ainda deve considerar varum fator não em termos de desempenho. Certamente não é como usar uma dynamicvariável. Mas dizer que nunca faz diferença pode estar exagerando.

Joel Coehoorn
fonte
23
Não apenas a IL deve ser idêntica - é idêntica. vari = 42; compila exatamente o mesmo código que int i = 42;
Brian Rasmussen
15
@BrianRasmussen: Eu sei que sua postagem é antiga, é antiga, mas eu suponho var i = 42;(o tipo de informação é int) NÃO é idêntico a long i = 42;. Portanto, em alguns casos, você pode estar fazendo suposições incorretas sobre a inferência de tipo. Isso pode causar erros de tempo de execução indescritíveis / de arestas caso o valor não se ajuste. Por esse motivo, ainda pode ser uma boa ideia ser explícito quando o valor não tiver um tipo explícito. Por exemplo, var x = new List<List<Dictionary<int, string>()>()>()seria aceitável, mas var x = 42é um tanto ambíguo e deve ser escrito como int x = 42. Mas cada um na sua ...
Nelson Rothermel
50
@ NelsonRothermel: var x = 42; não é ambíguo. Literais inteiros são do tipo int. Se você quer um longo literal, você escreve var x = 42L;.
Brian Rasmussen
6
Uhm, o que significa IL em c #? Eu realmente nunca ouvi falar disso.
precisa saber é
15
No seu exemplo das três linhas de código que se comportam de maneira diferente, a primeira linha não é compilada . A segunda e a terceira linhas, que são compiladas, fazem exatamente a mesma coisa. Se Fooretornado a List, em vez de um IList, todas as três linhas serão compiladas, mas a terceira linha se comportará como a primeira linha , não a segunda.
Servy
72

Como Joel diz, o compilador calcula em tempo de compilação o tipo var, efetivamente é apenas um truque que o compilador executa para salvar pressionamentos de tecla, por exemplo, por exemplo

var s = "hi";

é substituído por

string s = "hi";

pelo compilador antes de qualquer IL ser gerada. O IL gerado será exatamente o mesmo que se você tivesse digitado a string.

ljs
fonte
26

Como ninguém mencionou refletor ainda ...

Se você compilar o seguinte código C #:

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

Então use o refletor nele, você obtém:

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

Portanto, a resposta é claramente sem desempenho de tempo de execução atingido!

RichardOD
fonte
17

Para o seguinte método:

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

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

A saída IL é 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
Roubar
fonte
14

O compilador C # infere o tipo verdadeiro da varvariável em tempo de compilação. Não há diferença na IL gerada.

Michael Burr
fonte
14

Portanto, para deixar claro, é um estilo de codificação preguiçoso. Eu prefiro tipos nativos, dada a escolha; Vou usar esse "ruído" extra para garantir que estou escrevendo e lendo exatamente o que acho que estou no momento do código / depuração. * dar de ombros *

ChrisH
fonte
1
Essa é apenas a sua visão subjetiva e não uma resposta para a pergunta sobre desempenho. A resposta certa é que isso não afeta o desempenho. Votei no fim
Anders
Isso não responde à questão de se varafeta o desempenho; você está apenas afirmando sua opinião sobre se as pessoas devem usá-lo.
Herohtar 30/11/19
Inferir o tipo do valor posteriormente, por exemplo, alternar de int 5 para float 5.25, pode causar absolutamente problemas de desempenho. * shrug *
ChrisH
Não, isso não causará problemas de desempenho; você receberá erros de compilação em qualquer lugar que esperasse uma variável do tipo intporque ela não pode converter automaticamente o arquivo float, mas é exatamente o mesmo que aconteceria se você explicitamente usasse inte depois alterasse para float. De qualquer forma, sua resposta ainda não responde à pergunta "o uso varafeta o desempenho?" (particularmente em termos de IL gerada)
Herohtar 5/12/19
8

Acho que você não entendeu direito o que leu. Se ele é compilado para o tipo correto, então não é nenhuma diferença. Quando eu faço isso:

var i = 42;

O compilador sabe que é um int e gera código como se eu tivesse escrito

int i = 42;

Como diz a postagem que você vinculou, ela é compilada para o mesmo tipo. Não é uma verificação de tempo de execução ou qualquer outra coisa que exija código extra. O compilador apenas descobre qual deve ser o tipo e o usa.

jalf
fonte
Certo, mas e se mais tarde você i = i - someVar e someVar = 3.3. Eu sou um Int, agora. É melhor ser explícito não apenas para dar ao compilador uma vantagem inicial na busca de falhas, mas também para minimizar erros de tempo de execução ou conversões de tipo com lentidão no processo. * shrug * Também melhora o código para se auto-descrever. Eu venho fazendo isso há muito, muito tempo. Vou pegar o código "barulhento" com tipos explícitos sempre, de acordo com a escolha.
31719 ChrisH
5

Não há custo de desempenho de tempo de execução no uso de var. No entanto, eu suspeitaria que haja um custo de desempenho de compilação, pois o compilador precisa inferir o tipo, embora isso provavelmente seja insignificante.

Brian Rudolph
fonte
10
o RHS deve ter seu tipo calculado de qualquer maneira - o compilador pegaria tipos incompatíveis e geraria um erro, portanto, não é realmente um custo lá, eu acho.
10134 Jimmy
3

Se o compilador puder fazer inferências automáticas de tipo, não haverá nenhum problema com o desempenho. Ambos irão gerar o mesmo código

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

no entanto, se você estiver construindo o tipo dinamicamente (LINQ ...), então varé sua única pergunta e há outro mecanismo para comparar, a fim de dizer qual é a penalidade.

azulado
fonte
3

Eu sempre uso a palavra var em artigos da web ou orienta escritos.

A largura do editor de texto do artigo online é pequena.

Se eu escrever isso:

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

Você verá que o texto do código pré renderizado acima é muito longo e sai da caixa; ele fica oculto. O leitor precisa rolar para a direita para ver a sintaxe completa.

É por isso que eu sempre uso a palavra-chave var nos escritos de artigos da web.

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

Todo o código pré renderizado se encaixa na tela.

Na prática, para declarar objetos, raramente uso var, confio no intellisense para declarar objetos mais rapidamente.

Exemplo:

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

Mas, para retornar objetos de um método, uso var para escrever código mais rapidamente.

Exemplo:

var coolObject = GetCoolObject(param1, param2);
mjb
fonte
Se você está escrevendo para os alunos, coma sua própria comida de cachorro e sempre a escreva da mesma maneira "correta", de forma consistente. Os alunos geralmente levam as coisas 100% literalmente e a sério e começam a usar os hábitos desleixados que adquirem ao longo do caminho. $ 0,02
ChrisH
1

"var" é uma daquelas coisas que as pessoas amam ou odeiam (como regiões). Embora, diferentemente das regiões, var seja absolutamente necessário ao criar classes anônimas.

Para mim, var faz sentido quando você está atualizando um objeto diretamente como:

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

Dito isto, você pode facilmente fazer:

Dictionary<string, string> dict = O novo e o intellisense preencherão o resto para você aqui.

Se você deseja trabalhar apenas com uma interface específica, não poderá usar var, a menos que o método que você está chamando retorne a interface diretamente.

O compartilhador parece estar do lado de usar "var" por toda parte, o que pode levar mais pessoas a fazer dessa maneira. Mas eu meio que concordo que é mais difícil ler se você está chamando um método e não é óbvio o que está sendo retornado pelo nome.

O var não reduz a velocidade das coisas, mas há uma ressalva sobre isso que muitas pessoas não pensam. Se você fizer var result = SomeMethod();isso, o código a seguir espera algum tipo de resultado, onde você chamaria vários métodos ou propriedades ou qualquer outra coisa. Se você SomeMethod()mudou sua definição para outro tipo, mas ainda cumpriu o contrato que o outro código esperava, você acabou de criar um bug muito desagradável (se não houver testes de unidade / integração, é claro).

Daniel Lorenz
fonte
0

Depende da situação, se você tentar usar, este código abaixo.

A expressão é convertida em "OBJECT" e diminui muito o desempenho, mas é um problema isolado.

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 acima com 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 você deseja executar este código, use o código abaixo e obtenha a diferença de tempos.

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

Saudações

Silvio Garcez
fonte