Apakah menggunakan 'var' akan memengaruhi kinerja?

230

Sebelumnya saya mengajukan pertanyaan tentang mengapa saya melihat begitu banyak contoh menggunakan varkata kunci dan mendapatkan jawaban bahwa sementara itu hanya diperlukan untuk jenis anonim, namun tetap digunakan untuk membuat kode penulisan 'lebih cepat' / lebih mudah dan 'hanya karena'.

Mengikuti tautan ini ("C # 3.0 - Var Isn't Objec") Saya melihat bahwa vardikompilasi ke tipe yang benar di IL (Anda akan melihatnya tentang artikel di tengah jalan).

Pertanyaan saya adalah berapa banyak lagi, jika ada, kode IL tidak menggunakan varkata kunci, dan apakah akan mendekati tingkat yang terukur pada kinerja kode jika digunakan di mana-mana?

Jeff Keslinke
sumber
1
pertanyaan dijawab berabad-abad yang lalu, hanya ingin menambahkan satu hal lagi terhadap var - meskipun diselesaikan pada waktu kompilasi, itu tidak terlihat dengan benar oleh Visual Studio "Temukan Semua Referensi" dan "Cari Penggunaan" dari Resharper jika Anda ingin menemukan semua penggunaan jenis itu. - dan itu tidak akan diperbaiki karena terlalu lambat.
KolA
@KolA Variabel dideklarasikan dengan varpasti bekerja dengan "Temukan Semua Referensi" di Visual Studio 2019, jadi jika itu pernah rusak itu telah diperbaiki. Tetapi saya dapat mengonfirmasi bahwa itu berfungsi sejauh Visual Studio 2012, jadi saya tidak yakin mengapa Anda mengklaim itu tidak berfungsi.
Herohtar
@Herohtar coba ikuti kode "class X {} X GetX () {return new X ();} batal UseX () {var x = GetX ();}" dan Temukan Semua Referensi ke X, "var x = GetX ( ) "bit tidak disorot - dalam VS2019 terbaru seperti sekarang, inilah yang saya maksud. Itu disorot meskipun jika Anda menggunakan "X x = GetX ()" bukannya var
KolA
1
@ Kola Ah, saya mengerti maksud Anda - vartidak akan dianggap sebagai referensi Xketika Anda menggunakan "Temukan Semua Referensi" di X. Menariknya, jika Anda menggunakan "Temukan Semua Referensi" di vardalam pernyataan itu, itu akan menunjukkan kepada Anda referensi X(meskipun masih tidak mencantumkan varpernyataan). Selain itu, ketika kursor aktif var, kursor akan menyorot semua contoh Xdalam dokumen yang sama (dan sebaliknya).
Herohtar

Jawaban:

316

Tidak ada kode IL tambahan untuk varkata kunci: IL yang dihasilkan harus identik untuk jenis non-anonim. Jika kompiler tidak dapat membuat IL itu karena tidak dapat mengetahui tipe apa yang ingin Anda gunakan, Anda akan mendapatkan kesalahan kompiler.

Satu-satunya trik adalah bahwa varakan menyimpulkan jenis yang tepat di mana Anda mungkin telah memilih jenis antarmuka atau orang tua jika Anda mengatur jenis secara manual.


Perbarui 8 Tahun Kemudian

Saya perlu memperbarui ini karena pemahaman saya telah berubah. Saya sekarang percaya mungkin untuk varmempengaruhi kinerja dalam situasi di mana metode mengembalikan antarmuka, tetapi Anda akan menggunakan tipe yang tepat. Misalnya, jika Anda memiliki metode ini:

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

Pertimbangkan tiga baris kode ini untuk memanggil metode:

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

Ketiganya mengkompilasi dan mengeksekusi seperti yang diharapkan. Namun, dua baris pertama tidak persis sama, dan baris ketiga akan cocok dengan yang kedua, bukan yang pertama. Karena tanda tangan Foo()adalah untuk mengembalikan suatu IList<int>, itu adalah bagaimana kompiler akan membangun bar3variabel.

Dari sudut pandang kinerja, sebagian besar Anda tidak akan melihat. Namun, ada situasi di mana kinerja baris ketiga mungkin tidak secepat kinerja yang pertama . Saat Anda terus menggunakan bar3variabel, kompiler mungkin tidak dapat mengirimkan panggilan metode dengan cara yang sama.

Perhatikan bahwa mungkin (bahkan mungkin) jitter akan dapat menghapus perbedaan ini, tetapi tidak dijamin. Secara umum, Anda masih harus mempertimbangkan varuntuk menjadi non-faktor dalam hal kinerja. Sama sekali tidak seperti menggunakan dynamicvariabel. Tetapi untuk mengatakan itu tidak pernah membuat perbedaan sama sekali mungkin melebih-lebihkan itu.

Joel Coehoorn
sumber
23
Tidak hanya harus IL identik - itu adalah identik. var i = 42; mengkompilasi kode yang persis sama dengan int i = 42;
Brian Rasmussen
15
@BrianRasmussen: Saya tahu posting Anda sudah lama sudah tua, tapi saya berasumsi var i = 42;(infers tipe int) TIDAK identik dengan long i = 42;. Jadi dalam beberapa kasus Anda mungkin membuat asumsi yang salah tentang inferensi tipe. Ini dapat menyebabkan kesalahan runtime case sulit dipahami / tepi jika nilai tidak cocok. Karena alasan itu, mungkin masih merupakan ide yang baik untuk menjadi eksplisit ketika nilainya tidak memiliki tipe eksplisit. Jadi misalnya, var x = new List<List<Dictionary<int, string>()>()>()akan dapat diterima, tetapi var x = 42agak ambigu dan harus ditulis sebagai int x = 42. Tetapi untuk masing-masing mereka sendiri ...
Nelson Rothermel
50
@NelsonRothermel: var x = 42; tidak ambigu. Literal integer adalah tipe int. Jika Anda ingin panjang literal Anda menulis var x = 42L;.
Brian Rasmussen
6
Uhm apa IL berdiri untuk di C #? Saya tidak pernah benar-benar mendengarnya.
puretppc
15
Dalam contoh Anda dari 3 baris kode yang berperilaku berbeda, baris pertama tidak dikompilasi . Garis kedua dan ketiga, yang keduanya melakukan kompilasi, melakukan hal yang sama. Jika Foomengembalikan a List, daripada a IList, maka ketiga baris akan mengkompilasi tetapi baris ketiga akan berperilaku seperti baris pertama , bukan yang kedua.
Servy
72

Seperti kata Joel, kompiler bekerja pada waktu kompilasi jenis var apa yang seharusnya, secara efektif itu hanya sebuah trik yang dilakukan oleh kompiler untuk menghemat penekanan tombol, jadi misalnya

var s = "hi";

digantikan oleh

string s = "hi";

oleh kompiler sebelum IL dihasilkan. IL yang Dihasilkan akan persis sama seperti jika Anda mengetikkan string.

ls
sumber
26

Karena belum ada yang menyebut reflektor ...

Jika Anda mengkompilasi kode C # berikut:

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

Kemudian gunakan reflektor di atasnya, Anda mendapatkan:

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

Jadi jawabannya jelas tidak ada kinerja runtime hit!

RichardOD
sumber
17

Untuk metode berikut:

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

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

Output IL adalah ini:

        {
          .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
rampok
sumber
14

Compiler C # menyimpulkan tipe sebenarnya dari varvariabel pada waktu kompilasi. Tidak ada perbedaan dalam IL yang dihasilkan.

Michael Burr
sumber
14

Jadi, untuk lebih jelasnya, ini adalah gaya pengkodean yang malas. Saya lebih suka tipe asli, diberi pilihan; Saya akan mengambil sedikit tambahan "noise" untuk memastikan saya menulis dan membaca apa yang saya pikir saya pada waktu kode / debug. * mengangkat bahu *

Haha
sumber
1
Itu hanya pandangan subjektif Anda dan bukan jawaban untuk pertanyaan tentang kinerja. Jawaban yang benar adalah bahwa hal itu tidak berdampak pada kinerja. Saya memilih dekat
Anders
Ini tidak menjawab pertanyaan apakah varmempengaruhi kinerja sama sekali; Anda hanya menyatakan pendapat Anda tentang apakah orang harus menggunakannya.
Herohtar
Jenis inferring dari nilai nanti, misalnya, beralih dari int 5 ke float 5.25, benar-benar dapat menyebabkan masalah kinerja. * mengangkat bahu *
ChrisH
Tidak, itu tidak akan menyebabkan masalah kinerja; Anda akan mendapatkan galat build di tempat mana pun yang mengharapkan variabel tipe intkarena itu tidak dapat secara otomatis mengonversinya float, tapi itu persis sama dengan yang akan terjadi jika Anda secara eksplisit digunakan intdan kemudian diubah menjadi float. Bagaimanapun, jawaban Anda masih tidak menjawab pertanyaan "apakah menggunakan varmempengaruhi kinerja?" (khususnya dalam hal IL yang dihasilkan)
Herohtar
8

Saya pikir Anda tidak mengerti apa yang Anda baca. Jika akan dikompilasi untuk jenis yang tepat, maka adalah tidak ada perbedaan. Ketika saya melakukan ini:

var i = 42;

Kompiler tahu itu int, dan menghasilkan kode seolah-olah saya telah menulis

int i = 42;

Seperti yang Anda tautkan dengan posting, itu dikompilasi dengan tipe yang sama. Ini bukan pemeriksaan runtime atau apa pun yang membutuhkan kode tambahan. Kompilator hanya mengetahui apa jenisnya, dan menggunakannya.

jalf
sumber
Benar, tetapi bagaimana jika nanti Anda i = i - someVar dan someVar = 3.3. saya adalah seorang Int, sekarang. Lebih baik untuk menjadi eksplisit tidak hanya untuk memberikan compiler mulai menemukan kelemahan, tetapi juga untuk meminimalkan kesalahan runtime atau konversi jenis proses-memperlambat. * Mengangkat bahu * Ini juga membuat kode lebih baik untuk menggambarkan diri sendiri. Saya sudah melakukan ini sejak lama. Saya akan mengambil kode "berisik" dengan tipe eksplisit setiap kali, diberikan pilihan.
ChrisH
5

Tidak ada biaya kinerja runtime untuk menggunakan var. Meskipun, saya menduga ada biaya kinerja kompilasi karena kompiler perlu menyimpulkan jenisnya, meskipun ini kemungkinan besar akan diabaikan.

Brian Rudolph
sumber
10
RHS harus menghitung tipenya - kompiler akan menangkap tipe yang tidak cocok dan melemparkan kesalahan, jadi tidak benar-benar biaya di sana, saya pikir.
Jimmy
3

Jika kompiler dapat melakukan inferencing tipe otomatis, maka tidak akan ada masalah dengan kinerja. Keduanya akan menghasilkan kode yang sama

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

Namun, jika Anda membangun tipe secara dinamis (LINQ ...) maka itu varadalah satu-satunya pertanyaan Anda dan ada mekanisme lain untuk membandingkan untuk mengatakan apa hukumannya.

kebiruan
sumber
3

Saya selalu menggunakan kata var dalam artikel web atau memandu tulisan.

Lebar editor teks artikel online kecil.

Jika saya menulis ini:

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

Anda akan melihat bahwa teks kode pra yang dirender di atas terlalu panjang dan keluar dari kotak, sehingga disembunyikan. Pembaca perlu menggulir ke kanan untuk melihat sintaksis lengkap.

Itu sebabnya saya selalu menggunakan kata kunci var dalam tulisan artikel web.

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

Seluruh kode pra yang diberikan hanya muat di dalam layar.

Dalam praktiknya, untuk mendeklarasikan objek, saya jarang menggunakan var, saya mengandalkan intellisense untuk mendeklarasikan objek lebih cepat.

Contoh:

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

Tapi, untuk mengembalikan objek dari suatu metode, saya menggunakan var untuk menulis kode lebih cepat.

Contoh:

var coolObject = GetCoolObject(param1, param2);
mjb
sumber
Jika Anda menulis untuk siswa, maka makanlah makanan anjing Anda sendiri dan selalu tulis dengan cara yang "benar" yang sama, secara konsisten. Siswa sering mengambil hal-hal 100% kata demi kata dan untuk hati, dan akan mulai menggunakan kebiasaan ceroboh yang mereka ambil sepanjang jalan. $ 0,02
ChrisH
1

"var" adalah salah satu hal yang disukai atau dibenci orang (seperti wilayah). Padahal, tidak seperti wilayah, var mutlak diperlukan saat membuat kelas anonim.

Bagi saya, var masuk akal ketika Anda membuat objek secara langsung seperti:

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

Yang sedang berkata, Anda dapat dengan mudah melakukan:

Dictionary<string, string> dict = baru dan intellisense akan mengisi sisanya untuk Anda di sini.

Jika Anda hanya ingin bekerja dengan antarmuka tertentu, maka Anda tidak dapat menggunakan var kecuali metode yang Anda panggil mengembalikan antarmuka secara langsung.

Resharper tampaknya berada di sisi menggunakan "var" di seluruh, yang dapat mendorong lebih banyak orang untuk melakukannya dengan cara itu. Tetapi saya agak setuju bahwa lebih sulit untuk membaca jika Anda memanggil suatu metode dan tidak jelas apa yang dikembalikan oleh namanya.

var sendiri tidak memperlambat apa pun, tetapi ada satu peringatan untuk ini yang tidak dipikirkan banyak orang. Jika Anda melakukannya var result = SomeMethod();maka kode setelah itu mengharapkan semacam hasil kembali di mana Anda akan memanggil berbagai metode atau properti atau apa pun. Jika SomeMethod()mengubah definisinya menjadi beberapa tipe lain tetapi masih memenuhi kontrak yang diharapkan oleh kode lain, Anda baru saja membuat bug yang sangat jahat (jika tidak ada pengujian unit / integrasi, tentu saja).

Daniel Lorenz
sumber
0

Tergantung situasinya, jika Anda mencoba menggunakan, kode ini di bawah.

Ekspresi dikonversi ke "OBJECT" dan mengurangi begitu banyak kinerja, tetapi ini masalah tersendiri.

KODE:

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

Di atas hasil dengan 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();
}

Jika Anda ingin menjalankan kode ini gunakan kode di bawah ini, dan dapatkan perbedaan waktunya.

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

Salam

Silvio Garcez
sumber