การใช้ 'var' จะส่งผลกระทบต่อประสิทธิภาพหรือไม่


230

ก่อนหน้านี้ฉันถามคำถามเกี่ยวกับสาเหตุที่ฉันเห็นตัวอย่างมากมายที่ใช้varคำหลักและได้รับคำตอบว่าในขณะที่จำเป็นเฉพาะกับประเภทที่ไม่ระบุตัวตนเท่านั้น แต่มันถูกใช้เพื่อทำให้การเขียนโค้ด 'เร็วขึ้น' / ง่ายขึ้นและ 'เพียงเพราะ'

ต่อไปนี้การเชื่อมโยงนี้ ( "C # 3.0 - Var ไม่ได้ objec")ผมเห็นว่าvarได้รับการรวบรวมลงไปชนิดที่ถูกต้องในอิลลินอยส์ (คุณจะเห็นมันเกี่ยวกับตรงกลางลงบทความ)

คำถามของฉันคือยิ่งกว่านั้นหากมีรหัส IL ใช้varคำหลักนั้นและจะใกล้เคียงกับการวัดระดับประสิทธิภาพของรหัสหากมีการใช้ทุกที่หรือไม่


1
ตอบคำถามทุกเพศทุกวัยที่ผ่านมาเพียงแค่ต้องการที่จะเพิ่มสิ่งอื่น ๆ อีกมากมายกับ var - แม้จะมีการแก้ไขในเวลารวบรวมมันไม่ได้ถูกพบอย่างถูกต้องโดย Visual Studio ของ "ค้นหาทั้งหมดอ้างอิง" และ Resharper ของ "ค้นหาประเพณี" ถ้าคุณต้องการค้นหาประเพณีทั้งหมด - และมันจะไม่ได้รับการแก้ไขเพราะมันจะช้าเกินไป
KolA

@KolA Variables ประกาศโดยvarทำงานกับ "Find All Reference" ใน Visual Studio 2019 เป็นอย่างมากดังนั้นหากมีการแตกหักมันจะได้รับการแก้ไข แต่ฉันสามารถยืนยันได้ว่ามันทำงานได้ไกลเท่า Visual Studio 2012 ดังนั้นฉันไม่แน่ใจว่าทำไมคุณถึงอ้างว่ามันไม่ทำงาน
Herohtar

@Herohtar ลองใช้รหัสต่อไปนี้ "class X {} X GetX () {return new X ();} ถือเป็นโมฆะ UseX () {var x = GetX ();}" และค้นหาการอ้างอิงทั้งหมดไปที่ X "var x = GetX ( ) "บิตไม่ได้ถูกเน้น - ใน VS2019 ล่าสุด ณ ตอนนี้นี่คือสิ่งที่ฉันหมายถึง มันจะเน้นว่าถ้าคุณใช้ "X x = GetX ()" แทน var
KolA

1
@KolA โอ้ฉันเห็นสิ่งที่คุณหมายถึง - varจะไม่ได้รับการพิจารณาการอ้างอิงถึงXเมื่อคุณใช้ "ดูทุกอ้างอิง" Xบน ที่น่าสนใจถ้าคุณใช้ "ค้นหาการอ้างอิงทั้งหมด" varในคำสั่งนั้นมันจะแสดงการอ้างอิงถึงคุณX(แม้ว่ามันจะยังไม่แสดงรายการvarคำสั่ง) นอกจากนี้เมื่อเคอร์เซอร์เปิดอยู่varมันจะไฮไลต์อินสแตนซ์ทั้งหมดของXในเอกสารเดียวกัน (และในทางกลับกัน)
Herohtar

คำตอบ:


316

ไม่มีรหัส IL พิเศษสำหรับvarคำหลัก: IL ผลลัพธ์ควรเหมือนกันสำหรับประเภทที่ไม่ระบุชื่อ หากคอมไพเลอร์ไม่สามารถสร้าง IL นั้นได้เพราะมันไม่สามารถระบุประเภทที่คุณต้องการใช้คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์

เคล็ดลับเดียวคือว่าvarจะอนุมานประเภทที่แน่นอนที่คุณอาจเลือก Interface หรือผู้ปกครองประเภทหากคุณต้องตั้งประเภทด้วยตนเอง


อัพเดท 8 ปีต่อมา

ฉันต้องอัปเดตสิ่งนี้เนื่องจากความเข้าใจของฉันเปลี่ยนไป ตอนนี้ฉันเชื่อว่าอาจเป็นไปได้ที่varจะมีผลต่อประสิทธิภาพในสถานการณ์ที่เมธอดส่งคืนอินเตอร์เฟส แต่คุณควรใช้ประเภทที่แน่นอน ตัวอย่างเช่นหากคุณมีวิธีนี้:

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

พิจารณารหัสสามบรรทัดนี้เพื่อเรียกใช้เมธอด:

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

ทั้งสามรวบรวมและดำเนินการตามที่คาดไว้ อย่างไรก็ตามสองบรรทัดแรกนั้นไม่เหมือนกันและบรรทัดที่สามจะตรงกับที่สองแทนที่จะเป็นที่หนึ่ง เนื่องจากลายเซ็นของFoo()คือการส่งคืนIList<int>นั่นคือวิธีที่คอมไพเลอร์จะสร้างbar3ตัวแปร

จากมุมมองประสิทธิภาพส่วนใหญ่คุณจะไม่สังเกตเห็น แต่มีสถานการณ์ที่ประสิทธิภาพการทำงานของเส้นที่สามไม่อาจจะค่อนข้างเร็วที่สุดเท่าที่ประสิทธิภาพการทำงานของแรก ในขณะที่คุณใช้bar3ตัวแปรต่อไปคอมไพเลอร์อาจไม่สามารถส่งวิธีการเรียกด้วยวิธีเดียวกัน

โปรดทราบว่าเป็นไปได้ (น่าจะเป็นไปได้ด้วยซ้ำ) แต่กระวนกระวายใจจะสามารถลบความแตกต่างนี้ได้ แต่ไม่รับประกัน โดยทั่วไปคุณควรพิจารณาว่าvarไม่ใช่ปัจจัยด้านประสิทธิภาพ แน่นอนว่ามันไม่เหมือนกับการใช้dynamicตัวแปร แต่การที่จะบอกว่ามันไม่เคยสร้างความแตกต่างเลยอาจเป็นการพูดเกินจริง


23
ไม่เพียง แต่ IL ควรจะเหมือนกัน - มันเป็นเหมือนกัน var i = 42; คอมไพล์รหัสตรงทั้งหมดกับ int i = 42;
Brian Rasmussen

15
@BrianRasmussen: ฉันรู้ว่าการโพสต์ของคุณเก่าเก่า แต่ผมถือว่าvar i = 42;(ชนิดอนุมานเป็น int) long i = 42;ไม่เหมือนกัน ดังนั้นในบางกรณีคุณอาจใช้สมมติฐานที่ไม่ถูกต้องเกี่ยวกับการอนุมานประเภท สิ่งนี้อาจทำให้เกิดข้อผิดพลาดรันไทม์ของกรณีที่เข้าใจยาก / edge หากค่าไม่พอดี ด้วยเหตุผลดังกล่าวอาจเป็นความคิดที่ดีที่จะต้องระบุอย่างชัดเจนเมื่อค่าไม่มีประเภทที่ชัดเจน ดังนั้นสำหรับตัวอย่างเช่นvar x = new List<List<Dictionary<int, string>()>()>()จะเป็นที่ยอมรับ แต่ค่อนข้างคลุมเครือและควรจะเขียนเป็นvar x = 42 int x = 42แต่สำหรับพวกเขาแต่ละคน ...
เนลสันโรเธอร์เมล

50
@NelsonRothermel: var x = 42; ไม่ชัดเจน intตัวอักษรเป็นจำนวนเต็มประเภท var x = 42L;หากคุณต้องการที่แท้จริงที่มีความยาวที่คุณเขียน
Brian Rasmussen

6
อืม IL มีจุดยืนอย่างไรใน C #? ฉันไม่เคยได้ยินเรื่องนี้เลย
puretppc

15
ในตัวอย่างของคุณของ 3 บรรทัดของรหัสที่ทำงานแตกต่างกันในบรรทัดแรกไม่ได้รวบรวม ที่สองและสามสายซึ่งทั้งสองทำรวบรวมทำสิ่งเดียวกัน หากFooส่งคืน a Listแทนที่จะเป็นIListแล้วทั้งสามบรรทัดจะคอมไพล์ แต่บรรทัดที่สามจะทำตัวเหมือนบรรทัดแรกไม่ใช่บรรทัดที่สอง
Servy

72

ดังที่ Joel กล่าวว่าคอมไพเลอร์ทำงานได้ในเวลาคอมไพล์ประเภท var ควรมีประสิทธิภาพเป็นเพียงกลอุบายที่คอมไพเลอร์ทำงานเพื่อบันทึกการกดแป้นพิมพ์ดังนั้นตัวอย่าง

var s = "hi";

ถูกแทนที่ด้วย

string s = "hi";

โดยคอมไพเลอร์ก่อนสร้าง IL ใด ๆ Generated IL จะเหมือนกับที่คุณพิมพ์สตริง


26

อย่างที่ไม่มีใครพูดถึงตัวสะท้อนแสง ...

หากคุณรวบรวมรหัส C # ต่อไปนี้:

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

จากนั้นใช้ตัวสะท้อนแสงคุณจะได้รับ:

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

ดังนั้นคำตอบจึงชัดเจนว่าไม่มีการตีรันไทม์!


17

สำหรับวิธีการดังต่อไปนี้:

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

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

ผลลัพธ์ของ IL คือ:

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

คอมไพเลอร์ C # infers ชนิดที่แท้จริงของvarตัวแปรในเวลารวบรวม ไม่มีความแตกต่างใน IL ที่สร้างขึ้น


14

ดังนั้นเพื่อความชัดเจนมันเป็นสไตล์การเข้ารหัสที่ขี้เกียจ ฉันชอบประเภทชนพื้นเมืองให้เลือก ฉันจะใช้ "เสียง" เพิ่มอีกเล็กน้อยเพื่อให้แน่ใจว่าฉันกำลังเขียนและอ่านสิ่งที่ฉันคิดว่าฉันเป็นรหัส / เวลาดีบั๊ก * ยัก *


1
นั่นเป็นเพียงทัศนะส่วนตัวของคุณและไม่ใช่คำตอบสำหรับคำถามเกี่ยวกับประสิทธิภาพ คำตอบที่ถูกต้องคือไม่มีผลกระทบต่อประสิทธิภาพ ฉันโหวตให้ปิด
Anders

สิ่งนี้ไม่ได้ตอบคำถามว่าvarมีผลกับประสิทธิภาพหรือไม่ คุณเพียงแค่ระบุความคิดเห็นของคุณว่าบุคคลควรใช้หรือไม่
Herohtar

การอนุมานประเภทจากค่าในภายหลังตัวอย่างเช่นการสลับจาก int 5 เป็น float 5.25 อาจทำให้เกิดปัญหาเกี่ยวกับประสิทธิภาพได้ * * * * * * * * ยัก
ChrisH

ไม่นั่นจะไม่ทำให้เกิดปัญหาเรื่องประสิทธิภาพ คุณจะได้รับการสร้างข้อผิดพลาดในสถานที่ใด ๆ ที่ได้รับการคาดหวังว่าตัวแปรของชนิดintเพราะมันไม่สามารถโดยอัตโนมัติแปลงfloatแต่ที่ว่าสิ่งเดียวที่จะเกิดขึ้นถ้าคุณใช้อย่างชัดเจนและมีการเปลี่ยนแปลงไปแล้วint floatไม่ว่าในกรณีใดคำตอบของคุณยังไม่ตอบคำถามที่ว่า "การใช้varส่งผลกระทบต่อประสิทธิภาพหรือไม่" (โดยเฉพาะอย่างยิ่งในแง่ของการสร้าง IL)
Herohtar

8

ฉันคิดว่าคุณไม่เข้าใจสิ่งที่คุณอ่าน หากได้รับการรวบรวมเพื่อประเภทที่ถูกต้องแล้วมีคือไม่มีความแตกต่าง เมื่อฉันทำสิ่งนี้:

var i = 42;

คอมไพเลอร์รู้ว่ามันเป็น int และสร้างโค้ดราวกับว่าฉันได้เขียน

int i = 42;

ในขณะที่โพสต์ที่คุณเชื่อมโยงกับพูดว่าโพสต์นั้นได้รับการรวบรวมเป็นประเภทเดียวกัน ไม่ใช่การตรวจสอบรันไทม์หรืออะไรก็ตามที่ต้องการรหัสเพิ่มเติม คอมไพเลอร์เพิ่งจะทราบว่าประเภทต้องเป็นอะไรและใช้สิ่งนั้น


ถูกต้อง แต่จะเกิดอะไรขึ้นถ้าภายหลังคุณ = i - someVar และ someVar = 3.3 ตอนนี้ฉันเป็น Int เป็นการดีกว่าที่จะไม่เพียง แต่จะทำให้คอมไพเลอร์เริ่มต้นในการค้นหาข้อบกพร่อง แต่ยังเพื่อลดข้อผิดพลาดรันไทม์หรือการแปลงชนิดที่ทำให้กระบวนการช้าลง * ยัก * นอกจากนี้ยังทำให้รหัสดีขึ้นสำหรับการอธิบายตนเอง ฉันทำมานานแล้ว ฉันจะใช้รหัส "noisy" กับประเภทที่ชัดเจนทุกครั้งที่ได้รับเลือก
ChrisH

5

ไม่มีค่าใช้จ่ายประสิทธิภาพการทำงานขณะใช้งานกับการใช้ var แม้ว่าฉันจะสงสัยว่าจะมีค่าใช้จ่ายในการรวบรวมเนื่องจากคอมไพเลอร์จำเป็นต้องอนุมานประเภท แต่สิ่งนี้น่าจะเป็นสิ่งที่ละเลยไม่ได้


10
RHS ต้องมีการคำนวณชนิดของมันอย่างไรก็ตามผู้แปลจะจับประเภทที่ไม่ตรงกันและทำให้เกิดข้อผิดพลาดดังนั้นฉันคิดว่ามันไม่มีค่าใช้จ่าย
Jimmy

3

หากคอมไพเลอร์สามารถทำการอนุมานชนิดอัตโนมัติได้ก็จะไม่มีปัญหากับประสิทธิภาพการทำงาน ทั้งสองอย่างนี้จะสร้างรหัสเดียวกัน

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

อย่างไรก็ตามหากคุณกำลังสร้างประเภทแบบไดนามิก (LINQ ... ) แล้วvarเป็นคำถามเดียวของคุณและมีกลไกอื่น ๆ ที่จะเปรียบเทียบเพื่อที่จะพูดสิ่งที่เป็นโทษ


3

ฉันมักจะใช้คำว่า var ในบทความบนเว็บหรือคำแนะนำในการเขียน

ความกว้างของเครื่องมือแก้ไขข้อความของบทความออนไลน์มีขนาดเล็ก

ถ้าฉันเขียนสิ่งนี้:

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

คุณจะเห็นว่าข้อความโค้ดที่แสดงก่อนด้านบนยาวเกินไปและไหลออกมาจากกล่องข้อความจะถูกซ่อน ผู้อ่านต้องเลื่อนไปทางขวาเพื่อดูไวยากรณ์ทั้งหมด

นั่นเป็นเหตุผลที่ฉันมักจะใช้คำหลัก var ในการเขียนบทความเว็บ

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

รหัสที่แสดงผลล่วงหน้าทั้งหมดพอดีกับหน้าจอ

ในทางปฏิบัติสำหรับการประกาศวัตถุฉันไม่ค่อยใช้ var ฉันพึ่งพา intellisense เพื่อประกาศวัตถุได้เร็วขึ้น

ตัวอย่าง:

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

แต่สำหรับการคืนค่าอ๊อบเจคจากเมธอดฉันใช้ var เพื่อเขียนโค้ดเร็วขึ้น

ตัวอย่าง:

var coolObject = GetCoolObject(param1, param2);

หากคุณกำลังเขียนให้นักเรียนกินอาหารสุนัขของคุณเองแล้วเขียนวิธีการ "แก้ไข" ที่เหมือนกันเสมอ นักเรียนมักจะใช้คำต่อคำ 100% และเป็นหัวใจและจะเริ่มใช้นิสัยเลอะเทอะใด ๆ ที่พวกเขาเลือกตลอดทาง $ .02
ChrisH

1

"var" เป็นหนึ่งในสิ่งเหล่านั้นที่ผู้คนทั้งรักหรือเกลียด (เช่นภูมิภาค) แม้ว่าจะแตกต่างจากภูมิภาค แต่ var จำเป็นอย่างยิ่งเมื่อสร้างคลาสที่ไม่ระบุชื่อ

สำหรับฉันแล้ว var ทำให้รู้สึกเมื่อคุณกำลังสร้างวัตถุโดยตรงเช่น:

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

ที่ถูกกล่าวว่าคุณสามารถทำได้อย่างง่ายดาย:

Dictionary<string, string> dict = ใหม่และ Intellisense จะเติมส่วนที่เหลือให้คุณที่นี่

หากคุณต้องการทำงานกับอินเทอร์เฟซเฉพาะคุณไม่สามารถใช้ var เว้นแต่ว่าวิธีการโทรของคุณจะส่งคืนอินเตอร์เฟสโดยตรง

Resharper ดูเหมือนจะอยู่ด้านข้างของการใช้ "var" ทั่วซึ่งอาจผลักดันให้ผู้คนมากขึ้นที่จะทำอย่างนั้น แต่ฉันก็ยอมรับว่ามันยากที่จะอ่านถ้าคุณกำลังเรียกวิธีการและมันไม่ชัดเจนว่าสิ่งที่ถูกส่งกลับโดยชื่อ

ตัวเองไม่ได้ทำให้สิ่งต่าง ๆ ช้าลง แต่มีข้อแม้อย่างหนึ่งเกี่ยวกับเรื่องนี้ที่ไม่ให้คนจำนวนมากคิดถึง หากคุณทำเช่นvar result = SomeMethod();นั้นโค้ดหลังจากนั้นคาดว่าจะมีผลลัพธ์บางประเภทกลับมาซึ่งคุณจะเรียกวิธีการหรือคุณสมบัติต่าง ๆ หรืออะไรก็ตาม หากSomeMethod()เปลี่ยนคำจำกัดความเป็นประเภทอื่น แต่ยังคงปฏิบัติตามสัญญาที่รหัสอื่นคาดว่าจะเกิดขึ้นคุณเพิ่งสร้างข้อผิดพลาดที่น่ารังเกียจจริงๆ (หากไม่มีการทดสอบหน่วย / การรวมระบบ)


0

มันขึ้นอยู่กับสถานการณ์ถ้าคุณลองใช้รหัสนี้ร้อง

นิพจน์จะถูกแปลงเป็น "OBJECT" และลดประสิทธิภาพลงมาก แต่เป็นปัญหาที่แยกได้

รหัส:

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

ผลลัพธ์ที่เหนือกว่าด้วย 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();
}

หากคุณต้องการรันโค้ดนี้ให้ใช้รหัสตะโกนและรับความแตกต่างของเวลา

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

ความนับถือ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.