Việc sử dụng 'var' có ảnh hưởng đến hiệu suất không?


230

Trước đó tôi đã hỏi một câu hỏi về lý do tại sao tôi thấy rất nhiều ví dụ sử dụng vartừ khóa và nhận được câu trả lời rằng mặc dù chỉ cần thiết cho các loại ẩn danh, dù sao nó cũng được sử dụng để làm cho mã viết 'nhanh hơn' / dễ dàng hơn và 'chỉ vì'.

Theo liên kết này ("C # 3.0 - Var Is not Objec") Tôi thấy rằng nó varđược biên dịch thành loại chính xác trong IL (bạn sẽ thấy nó về bài viết giữa chừng).

Câu hỏi của tôi là có bao nhiêu, nếu có, mã IL sử dụng vartừ khóa, và nó thậm chí sẽ gần với mức có thể đo lường được về hiệu suất của mã nếu nó được sử dụng ở mọi nơi?


1
câu hỏi đã được trả lời từ lâu, chỉ muốn thêm một điều nữa chống lại var - mặc dù đã được giải quyết vào thời gian biên dịch nhưng nó không được phát hiện đúng bởi "Tìm tất cả các tài liệu tham khảo" và "Tìm lại cách sử dụng" của Resharper nếu bạn muốn tìm tất cả các cách sử dụng - và nó sẽ không được sửa chữa vì nó sẽ quá chậm.
KolA

@KolA Các biến được khai báo với varhoạt động chắc chắn nhất với "Tìm tất cả các tham chiếu" trong Visual Studio 2019, vì vậy nếu nó bị hỏng, nó đã được sửa. Nhưng tôi có thể xác nhận rằng nó hoạt động xa như Visual Studio 2012, vì vậy tôi không chắc tại sao bạn tuyên bố nó không hoạt động.
Herohtar

@Herohtar thử theo mã "class X {} X GetX () {return new X ();} void UseX () {var x = GetX ();}" và Tìm tất cả các tham chiếu đến X, "var x = GetX ( ) "bit không được tô sáng - trong VS2019 mới nhất tính đến thời điểm hiện tại, đây là ý tôi muốn nói. Nó được tô sáng mặc dù nếu bạn sử dụng "X x = GetX ()" thay vì var
KolA

1
@KolA À, tôi hiểu ý của bạn - varsẽ không được coi là tài liệu tham khảo Xkhi bạn sử dụng "Tìm tất cả tài liệu tham khảo" trên X. Thật thú vị, nếu bạn sử dụng "Tìm tất cả tài liệu tham khảo" vartrong câu lệnh đó, nó sẽ hiển thị cho bạn các tham chiếu đến X(mặc dù nó vẫn không liệt kê varcâu lệnh). Ngoài ra, khi con trỏ bật var, nó sẽ làm nổi bật tất cả các phiên bản Xtrong cùng một tài liệu (và ngược lại).
Herohtar

Câu trả lời:


316

Không có mã IL bổ sung cho var từ khóa: IL kết quả phải giống hệt với các loại không ẩn danh. Nếu trình biên dịch không thể tạo IL đó vì nó không thể tìm ra loại bạn định sử dụng, bạn sẽ gặp lỗi trình biên dịch.

Bí quyết duy nhất là varsẽ suy ra một loại chính xác trong đó bạn có thể đã chọn loại Giao diện hoặc loại phụ huynh nếu bạn đặt loại thủ công.


Cập nhật 8 năm sau

Tôi cần cập nhật điều này vì sự hiểu biết của tôi đã thay đổi. Bây giờ tôi tin rằng có thể varảnh hưởng đến hiệu suất trong trường hợp phương thức trả về giao diện, nhưng bạn đã sử dụng một loại chính xác. Ví dụ: nếu bạn có phương pháp này:

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

Hãy xem xét ba dòng mã này để gọi phương thức:

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

Tất cả ba biên dịch và thực hiện như mong đợi. Tuy nhiên, hai dòng đầu tiên không hoàn toàn giống nhau và dòng thứ ba sẽ khớp với dòng thứ hai, thay vì dòng thứ nhất. Bởi vì chữ ký của Foo()là để trả về một IList<int>, đó là cách trình biên dịch sẽ xây dựngbar3 biến.

Từ quan điểm hiệu suất, chủ yếu bạn sẽ không nhận thấy. Tuy nhiên, có những tình huống mà hiệu suất của dòng thứ ba có thể không hoàn toàn nhanh như hiệu suất của dòng đầu tiên . Khi bạn tiếp tục sử dụng bar3biến, trình biên dịch có thể không thể gửi các cuộc gọi phương thức theo cùng một cách.

Lưu ý rằng có thể (thậm chí là) jitter sẽ có thể xóa sự khác biệt này, nhưng nó không được bảo đảm. Nói chung, bạn vẫn nên coi varlà một yếu tố không quan trọng về hiệu suất. Nó chắc chắn không giống như sử dụng một dynamicbiến. Nhưng để nói rằng nó không bao giờ làm cho một sự khác biệt ở tất cả có thể được cường điệu hóa nó.


23
Không chỉ IL nên giống hệt nhau - đó giống hệt nhau. var i = 42; biên dịch thành mã chính xác như int i = 42;
Brian Rasmussen

15
@BrianRasmussen: Tôi biết bài viết của bạn đã cũ, nhưng tôi giả sử var i = 42;(kiểu infers là int) KHÔNG giống với long i = 42;. Vì vậy, trong một số trường hợp, bạn có thể đưa ra các giả định không chính xác về kiểu suy luận. Điều này có thể gây ra lỗi thời gian chạy trường hợp khó nắm bắt / cạnh nếu giá trị không phù hợp. Vì lý do đó, nó vẫn có thể là một ý tưởng tốt để rõ ràng khi giá trị không có loại rõ ràng. Vì vậy, ví dụ, var x = new List<List<Dictionary<int, string>()>()>()sẽ được chấp nhận, nhưng var x = 42hơi mơ hồ và nên được viết là int x = 42. Nhưng với mỗi người của họ ...
Nelson Rothermel

50
@NelsonRothermel: var x = 42; không mơ hồ. Chữ nguyên là loại int. Nếu bạn muốn một chữ dài bạn viết var x = 42L;.
Brian Rasmussen

6
Uhm IL đại diện cho điều gì trong C #? Tôi chưa bao giờ thực sự nghe về nó.
puretppc

15
Trong ví dụ của bạn về 3 dòng mã hoạt động khác nhau, dòng đầu tiên không biên dịch . Các dòng thứ hai và thứ ba, mà cả hai làm biên dịch, thực hiện chính xác những điều tương tự. Nếu Footrả về a List, thay vì một IList, thì cả ba dòng sẽ biên dịch nhưng dòng thứ ba sẽ hoạt động giống như dòng đầu tiên , không phải dòng thứ hai.
Phục vụ

72

Như Joel nói, trình biên dịch hoạt động vào thời gian biên dịch kiểu var nên là gì, thực ra đó chỉ là một mẹo mà trình biên dịch thực hiện để lưu các tổ hợp phím, ví dụ như vậy

var s = "hi";

được thay thế bởi

string s = "hi";

bởi trình biên dịch trước khi bất kỳ IL được tạo. Các IL tạo sẽ được chính xác giống như nếu bạn muốn chuỗi gõ.


26

Như chưa ai đề cập đến gương phản xạ ...

Nếu bạn biên dịch mã C # sau:

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

Sau đó sử dụng phản xạ trên nó, bạn nhận được:

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

Vì vậy, câu trả lời rõ ràng là không có hiệu suất thời gian chạy hit!


17

Đối với phương pháp sau:

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

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

Đầu ra IL là thế này:

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

Trình biên dịch C # nhập vào loại thực của varbiến tại thời gian biên dịch. Không có sự khác biệt trong IL được tạo.


14

Vì vậy, rõ ràng, đó là một phong cách mã hóa lười biếng. Tôi thích các loại bản địa, đưa ra sự lựa chọn; Tôi sẽ lấy thêm một chút "tiếng ồn" để đảm bảo rằng tôi đang viết và đọc chính xác những gì tôi nghĩ rằng tôi đang ở thời điểm mã / gỡ lỗi. * nhún vai *


1
Đó chỉ là quan điểm chủ quan của bạn và không phải là một câu trả lời cho câu hỏi về hiệu suất. Câu trả lời đúng là nó không có tác động đến hiệu suất. Tôi đã bỏ phiếu cho đóng
Anders

Điều này không trả lời câu hỏi liệu có varảnh hưởng đến hiệu suất hay không; bạn chỉ nêu quan điểm của bạn về việc mọi người có nên sử dụng nó hay không.
Herohtar

Ví dụ, suy ra loại từ giá trị sau đó, chuyển từ int 5 sang float 5.25, hoàn toàn có thể gây ra vấn đề về hiệu suất. * nhún *
ChrisH

Không, điều đó sẽ không gây ra bất kỳ vấn đề hiệu suất; bạn sẽ gặp lỗi xây dựng ở bất kỳ nơi nào mong đợi một biến loại intvì nó không thể tự động chuyển đổi float, nhưng đó chính xác là điều tương tự sẽ xảy ra nếu bạn sử dụng rõ ràng intvà sau đó đổi thành float. Trong mọi trường hợp, câu trả lời của bạn vẫn không trả lời câu hỏi "việc sử dụng có varảnh hưởng đến hiệu suất không?" (đặc biệt là về IL được tạo)
Herohtar

8

Tôi không nghĩ bạn hiểu đúng những gì bạn đọc. Nếu nó được biên soạn để đúng loại, sau đó có sự khác biệt. Khi tôi làm điều này:

var i = 42;

Trình biên dịch biết đó là một int và tạo mã như thể tôi đã viết

int i = 42;

Như bài đăng bạn liên kết để nói, nó được biên dịch theo cùng loại. Đây không phải là kiểm tra thời gian chạy hoặc bất cứ điều gì khác yêu cầu thêm mã. Trình biên dịch chỉ ra loại phải là gì và sử dụng nó.


Đúng, nhưng nếu sau này bạn i = i - someVar và someVar = 3.3. Tôi là một Int, bây giờ. Tốt hơn hết là không chỉ rõ ràng để giúp trình biên dịch bắt đầu tìm kiếm các lỗi, mà còn để giảm thiểu các lỗi thời gian chạy hoặc chuyển đổi loại làm chậm quá trình. * nhún * Nó cũng làm cho mã tốt hơn để tự mô tả. Tôi đã làm điều này rất lâu rồi. Tôi sẽ lấy mã "nhiễu" với các loại rõ ràng mỗi lần, đưa ra lựa chọn.
ChrisH

5

Không có chi phí hiệu năng thời gian chạy để sử dụng var. Mặc dù, tôi sẽ nghi ngờ rằng có một chi phí hiệu năng biên dịch vì trình biên dịch cần phải suy ra loại, mặc dù điều này rất có thể sẽ không đáng kể.


10
RHS phải có loại tính toán dù sao đi nữa - trình biên dịch sẽ bắt các loại không khớp và gây ra lỗi, vì vậy không thực sự là một chi phí ở đó, tôi nghĩ.
Jimmy

3

Nếu trình biên dịch có thể thực hiện kiểu suy luận tự động, thì sẽ không có vấn đề gì với hiệu suất. Cả hai sẽ tạo cùng một mã

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

tuy nhiên, nếu bạn đang xây dựng kiểu động (LINQ ...) thì đó varlà câu hỏi duy nhất của bạn và có cơ chế khác để so sánh để nói mức phạt là gì.


3

Tôi luôn sử dụng từ var trong các bài viết trên web hoặc hướng dẫn viết.

Độ rộng của trình soạn thảo văn bản của bài viết trực tuyến là nhỏ.

Nếu tôi viết điều này:

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

Bạn sẽ thấy rằng văn bản mã tiền được hiển thị ở trên quá dài và chảy ra khỏi hộp, nó bị ẩn đi. Người đọc cần cuộn sang bên phải để xem cú pháp đầy đủ.

Đó là lý do tại sao tôi luôn sử dụng từ khóa var trong các bài viết trên web.

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

Toàn bộ mã tiền được hiển thị vừa khít trong màn hình.

Trong thực tế, để khai báo đối tượng, tôi hiếm khi sử dụng var, tôi dựa vào intellisense để khai báo đối tượng nhanh hơn.

Thí dụ:

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

Nhưng, để trả về đối tượng từ một phương thức, tôi sử dụng var để viết mã nhanh hơn.

Thí dụ:

var coolObject = GetCoolObject(param1, param2);

Nếu bạn đang viết cho học sinh, thì hãy ăn thức ăn cho chó của riêng bạn và luôn viết nó theo cùng một cách "chính xác". Học sinh thường lấy những thứ nguyên văn 100% và thuộc nằm lòng, và sẽ bắt đầu sử dụng bất kỳ thói quen cẩu thả nào mà họ nhặt được trên đường đi. $ 0,02
ChrisH

1

"Var" là một trong những điều mà mọi người yêu hoặc ghét (như các vùng). Mặc dù, không giống như các vùng, var là hoàn toàn cần thiết khi tạo các lớp ẩn danh.

Đối với tôi, var có ý nghĩa khi bạn làm mới một đối tượng trực tiếp như:

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

Điều đó đang được nói, bạn có thể dễ dàng làm:

Dictionary<string, string> dict = mới và intellisense sẽ điền vào phần còn lại cho bạn ở đây.

Nếu bạn chỉ muốn làm việc với một giao diện cụ thể, thì bạn không thể sử dụng var trừ khi phương thức bạn đang gọi trả về giao diện trực tiếp.

Resharper dường như đứng về phía sử dụng "var" khắp nơi, điều này có thể thúc đẩy nhiều người làm theo cách đó. Nhưng tôi đồng ý rằng sẽ khó đọc hơn nếu bạn đang gọi một phương thức và không rõ ràng cái gì đang được trả về bởi tên đó.

Bản thân var không làm mọi thứ chậm lại, nhưng có một sự cảnh báo về điều này mà không nhiều người nghĩ đến. Nếu bạn làm như vậy var result = SomeMethod();thì mã sau đó đang mong đợi một loại kết quả nào đó trở lại nơi bạn gọi các phương thức hoặc thuộc tính khác nhau hoặc bất cứ điều gì. Nếu SomeMethod()thay đổi định nghĩa của nó thành một số loại khác nhưng nó vẫn đáp ứng hợp đồng mà mã khác đang mong đợi, bạn chỉ tạo ra một lỗi thực sự khó chịu (tất nhiên nếu không có kiểm tra đơn vị / tích hợp).


0

Tùy thuộc vào tình huống, nếu bạn thử sử dụng, mã này dưới đây.

Biểu thức được chuyển đổi thành "ĐỐI TƯỢNG" và giảm hiệu năng rất nhiều, nhưng đó là một vấn đề riêng lẻ.

MÃ:

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

Kết quả trên với 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();
}

Nếu bạn muốn thực thi mã này, hãy sử dụng mã dưới đây và nhận được sự khác biệt về thời gian.

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

Trân trọng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.