ทำไม TypedReference ถึงอยู่เบื้องหลัง มันเร็วและปลอดภัย…เกือบมีมนต์ขลัง!


128

คำเตือน: คำถามนี้เป็นเรื่องนอกรีตนักเขียนโปรแกรมทางศาสนาปฏิบัติตามแนวปฏิบัติที่ดีเสมอโปรดอย่าอ่าน :)

มีใครรู้บ้างไหมว่าทำไมการใช้TypedReferenceจึงเป็นสิ่งที่ทำให้หมดกำลังใจ (โดยปริยายโดยไม่มีเอกสารประกอบ)

ฉันพบการใช้งานที่ยอดเยี่ยมสำหรับมันเช่นเมื่อส่งพารามิเตอร์ทั่วไปผ่านฟังก์ชั่นที่ไม่ควรเป็นแบบทั่วไป (เมื่อใช้objectอาจจะ overkill หรือช้าถ้าคุณต้องการประเภทค่า) สำหรับเมื่อคุณต้องการตัวชี้ทึบแสงหรือ สำหรับเมื่อคุณต้องการเข้าถึงองค์ประกอบของอาร์เรย์ได้อย่างรวดเร็วซึ่งมีรายละเอียดที่คุณพบที่รันไทม์ (โดยใช้Array.InternalGetReference) เนื่องจาก CLR ไม่อนุญาตการใช้งานประเภทนี้อย่างไม่ถูกต้องทำไมจึงหมดกำลังใจ ดูเหมือนจะไม่ปลอดภัยหรืออะไร ...


การใช้งานอื่น ๆ ที่ฉันได้พบTypedReference:

Generic "เชี่ยวชาญ" ใน C # (นี่คือประเภทที่ปลอดภัย):

static void foo<T>(ref T value)
{
    //This is the ONLY way to treat value as int, without boxing/unboxing objects
    if (value is int)
    { __refvalue(__makeref(value), int) = 1; }
    else { value = default(T); }
}

เขียนโค้ดที่ทำงานร่วมกับตัวชี้ทั่วไป (นี้เป็นอย่างมากที่ไม่ปลอดภัยถ้าในทางที่ผิด แต่อย่างรวดเร็วและปลอดภัยหากใช้อย่างถูกต้อง):

//This bypasses the restriction that you can't have a pointer to T,
//letting you write very high-performance generic code.
//It's dangerous if you don't know what you're doing, but very worth if you do.
static T Read<T>(IntPtr address)
{
    var obj = default(T);
    var tr = __makeref(obj);

    //This is equivalent to shooting yourself in the foot
    //but it's the only high-perf solution in some cases
    //it sets the first field of the TypedReference (which is a pointer)
    //to the address you give it, then it dereferences the value.
    //Better be 10000% sure that your type T is unmanaged/blittable...
    unsafe { *(IntPtr*)(&tr) = address; }

    return __refvalue(tr, T);
}

การเขียนเวอร์ชันวิธีการsizeofสอนซึ่งอาจมีประโยชน์ในบางครั้ง:

static class ArrayOfTwoElements<T> { static readonly Value = new T[2]; }

static uint SizeOf<T>()
{
    unsafe 
    {
        TypedReference
            elem1 = __makeref(ArrayOfTwoElements<T>.Value[0] ),
            elem2 = __makeref(ArrayOfTwoElements<T>.Value[1] );
        unsafe
        { return (uint)((byte*)*(IntPtr*)(&elem2) - (byte*)*(IntPtr*)(&elem1)); }
    }
}

การเขียนวิธีการที่ผ่านพารามิเตอร์ "รัฐ" ที่ต้องการหลีกเลี่ยงการชกมวย:

static void call(Action<int, TypedReference> action, TypedReference state)
{
    //Note: I could've said "object" instead of "TypedReference",
    //but if I had, then the user would've had to box any value types
    try
    {
        action(0, state);
    }
    finally { /*Do any cleanup needed*/ }
}

เหตุใดจึงมีการใช้งานเช่นนี้ "หมดกำลังใจ" (โดยไม่มีเอกสารประกอบ) เหตุผลด้านความปลอดภัยโดยเฉพาะ? ดูเหมือนว่าจะปลอดภัยและตรวจสอบได้อย่างสมบูรณ์หากไม่ได้ผสมกับพอยน์เตอร์ (ซึ่งไม่ปลอดภัยหรือตรวจสอบได้)


ปรับปรุง:

โค้ดตัวอย่างเพื่อแสดงให้เห็นว่าTypedReferenceสามารถเร็วเป็นสองเท่า (หรือมากกว่า):

using System;
using System.Collections.Generic;
static class Program
{
    static void Set1<T>(T[] a, int i, int v)
    { __refvalue(__makeref(a[i]), int) = v; }

    static void Set2<T>(T[] a, int i, int v)
    { a[i] = (T)(object)v; }

    static void Main(string[] args)
    {
        var root = new List<object>();
        var rand = new Random();
        for (int i = 0; i < 1024; i++)
        { root.Add(new byte[rand.Next(1024 * 64)]); }
        //The above code is to put just a bit of pressure on the GC

        var arr = new int[5];
        int start;
        const int COUNT = 40000000;

        start = Environment.TickCount;
        for (int i = 0; i < COUNT; i++)
        { Set1(arr, 0, i); }
        Console.WriteLine("Using TypedReference:  {0} ticks",
                          Environment.TickCount - start);
        start = Environment.TickCount;
        for (int i = 0; i < COUNT; i++)
        { Set2(arr, 0, i); }
        Console.WriteLine("Using boxing/unboxing: {0} ticks",
                          Environment.TickCount - start);

        //Output Using TypedReference:  156 ticks
        //Output Using boxing/unboxing: 484 ticks
    }
}

(แก้ไข: ฉันแก้ไขเกณฑ์มาตรฐานข้างต้นเนื่องจากเวอร์ชันล่าสุดของโพสต์ใช้เวอร์ชันการแก้ไขข้อบกพร่องของรหัส [ฉันลืมเปลี่ยนเป็นรุ่น] และไม่กดดัน GC รุ่นนี้เป็นจริงมากขึ้นและ ในระบบของฉันมันเร็วกว่าสามเท่าTypedReferenceโดยเฉลี่ย)


เมื่อฉันเรียกใช้ตัวอย่างของคุณฉันได้รับผลลัพธ์ที่แตกต่างอย่างสิ้นเชิง TypedReference: 203 ticks, boxing/unboxing: 31 ticks. ไม่ว่าสิ่งที่ฉันลอง (รวมถึงวิธีที่แตกต่างกันในการทำเวลา) มวย / unboxing ยังคงเร็วขึ้นในระบบของฉัน
Seph

1
@Seph: ฉันเพิ่งเห็นความคิดเห็นของคุณ นั่นเป็นอย่างมากที่น่าสนใจ - ก็น่าจะได้เร็วขึ้นใน x64 แต่ช้าลงบน x86 แปลก ...
user541686

1
ฉันเพิ่งทดสอบโค้ดเบนช์มาร์กในเครื่อง x64 ของฉันภายใต้. NET 4.5 ฉันแทนที่ Environment.TickCount ด้วย Diagnostics.Stopwatch และไปด้วย ms แทนเห็บ ฉันวิ่งแต่ละบิลด์ (x86, 64, ใด ๆ ) สามครั้ง ผลลัพธ์ที่ดีที่สุดจากสามรายการคือ follws: x86: 205 / 27ms (ผลลัพธ์เดียวกันสำหรับ 2/3 รันในบิลด์นี้) x64: 218 / 109ms ใด ๆ : 205/27 มิลลิวินาที (ผลลัพธ์เดียวกันสำหรับ 2/3 วิ่งบนบิลด์นี้) ใน - ทุกกรณีกล่อง / unboxing เร็วขึ้น
kornman00

2
การวัดความเร็วแปลก ๆ อาจมาจากข้อเท็จจริงสองประการนี้: * (T) (วัตถุ) v ไม่ได้ทำการจัดสรรฮีปจริง ใน. NET 4+ มันได้รับการปรับปรุงให้ดีที่สุด ไม่มีการจัดสรรบนเส้นทางนี้และมันก็เร็วมาก * การใช้ makeref ต้องการให้มีการจัดสรรตัวแปรจริงในสแต็ก (ในขณะที่วิธี kinda-box อาจปรับให้เหมาะสมในการลงทะเบียน) ยิ่งไปกว่านั้นเมื่อดูที่การกำหนดเวลาฉันคิดว่ามันบั่นทอนการอินไลน์แม้จะมีการบังคับใช้ธงแบบอินไลน์ ดังนั้นครับกล่องเป็น inlined และ enregistered ขณะ makeref ทำให้สายงานและดำเนินงานกอง
hypersw

1
หากต้องการดูผลกำไรของการคัดเลือกนักแสดงให้ทำสิ่งเล็กน้อยให้น้อยลง เช่นการคัดเลือกนักแสดงที่อยู่ในประเภท enum (int -> DockStyle) กล่องนี้เป็นของจริงและช้าลงเกือบสิบเท่า
hypersw

คำตอบ:


42

คำตอบสั้น: การพกพา

ในขณะที่__arglist, __makerefและ__refvalueเป็นส่วนขยายของภาษาและไม่มีเอกสารใน C # ภาษาข้อมูลจำเพาะ, โครงสร้างที่ใช้ในการใช้พวกเขาภายใต้ประทุน (varargเรียกประชุมTypedReferenceชนิดarglist, refanytype, mkanyrefและrefanyvalคำแนะนำ) มีเอกสารอย่างสมบูรณ์แบบในCLI สเปกสินค้า (ECMA-335)ในห้องสมุด vararg

การกำหนดไว้ใน Vararg Library ทำให้เห็นได้ชัดว่าพวกเขามีวัตถุประสงค์หลักเพื่อสนับสนุนรายการอาร์กิวเมนต์ที่มีความยาวผันแปรได้และไม่มากนัก รายการอาร์กิวเมนต์ตัวแปรมีการใช้งานน้อยในแพลตฟอร์มที่ไม่จำเป็นต้องเชื่อมต่อกับรหัส C ภายนอกที่ใช้ varargs ด้วยเหตุผลนี้ไลบรารี Varargs จึงไม่ได้เป็นส่วนหนึ่งของโปรไฟล์ CLI ใด ๆ การใช้งาน CLI ที่ถูกกฎหมายอาจเลือกที่จะไม่สนับสนุนไลบรารี Varargs เนื่องจากไม่รวมอยู่ในโปรไฟล์เคอร์เนล CLI:

4.1.6 Vararg

ชุดคุณลักษณะ varargสนับสนุนรายการอาร์กิวเมนต์ตัวแปรที่มีความยาวและตัวชี้รันไทม์พิมพ์

หากละเว้น:ความพยายามใด ๆ ในการอ้างอิงวิธีการที่มีการvarargเรียกประชุมหรือการเข้ารหัสลายเซ็นที่เกี่ยวข้องกับวิธีการ vararg (ดู Partition II) จะโยนSystem.NotImplementedExceptionข้อยกเว้น วิธีใช้คำแนะนำ CIL arglist, refanytype,mkrefanyและrefanyvalจะโยนSystem.NotImplementedExceptionข้อยกเว้น ไม่มีการระบุช่วงเวลาที่แม่นยำของข้อยกเว้น ชนิดที่System.TypedReferenceไม่จำเป็นต้องกำหนด

อัปเดต (ตอบกลับGetValueDirectความคิดเห็น):

FieldInfo.GetValueDirectมีFieldInfo.SetValueDirectอยู่ไม่ได้เป็นส่วนหนึ่งของห้องสมุดชั้นฐาน โปรดสังเกตว่ามีความแตกต่างระหว่าง. NET Framework Class Library และ Base Class Library BCL เป็นสิ่งเดียวที่จำเป็นสำหรับการปฏิบัติตาม CLI / C # และมีการบันทึกไว้ในECMA TR / 84TR (อันที่จริงแล้วFieldInfoตัวมันเองเป็นส่วนหนึ่งของไลบรารี Reflection และไม่รวมอยู่ในโปรไฟล์เคอร์เนล CLI ด้วย)

ทันทีที่คุณใช้วิธีการนอก BCL คุณจะยอมแพ้การพกพาเล็กน้อย (และสิ่งนี้กำลังทวีความสำคัญมากยิ่งขึ้นเมื่อมีการเริ่มต้นของการใช้งานที่ไม่ใช่. NET CLI เช่น Silverlight และ MonoTouch) แม้ว่าการใช้งานต้องการเพิ่มความสามารถในการใช้งานร่วมกันได้กับไลบรารี่ Microsoft .NET Framework Class แต่ก็สามารถให้GetValueDirectและSetValueDirectรับTypedReferenceโดยไม่ต้องทำการTypedReferenceจัดการเป็นพิเศษโดยรันไทม์ (โดยทั่วไปทำให้เทียบเท่ากับไฟล์objectคู่กัน

หากพวกเขาบันทึกไว้ใน C # ก็จะมีผลกระทบอย่างน้อยสองสาม:

  1. เช่นเดียวกับคุณสมบัติใด ๆ ก็อาจกลายเป็นสิ่งกีดขวางบนถนนไปสู่ฟีเจอร์ใหม่โดยเฉพาะอย่างยิ่งเนื่องจากสิ่งนี้ไม่เหมาะกับการออกแบบของ C # และต้องการส่วนขยายไวยากรณ์แปลก ๆ และการส่งชนิดพิเศษโดยรันไทม์
  2. การใช้งานทั้งหมดของ C # ต้องใช้คุณลักษณะนี้อย่างใดและไม่จำเป็นต้องเป็นเรื่องเล็กน้อย / เป็นไปได้สำหรับการใช้งาน C # ที่ไม่ได้ทำงานบน CLI เลยหรือทำงานบน CLI โดยไม่ต้องใช้ Varargs

4
ข้อโต้แย้งที่ดีสำหรับการพกพา +1 แต่สิ่งที่เกี่ยวFieldInfo.GetValueDirectและFieldInfo.SetValueDirect? พวกมันเป็นส่วนหนึ่งของ BCL และใช้มันตามที่คุณต้องการ TypedReferenceดังนั้นโดยพื้นฐานแล้วมันไม่ได้บังคับTypedReferenceให้ต้องนิยามเสมอ (นอกจากนี้ยังมีหมายเหตุอื่น: แม้ว่าคำหลักจะไม่มีอยู่ตราบใดที่มีคำแนะนำอยู่คุณยังคงสามารถเข้าถึงได้โดยใช้วิธีการเปล่งแบบไดนามิก ... ตราบใดที่แพลตฟอร์มของคุณทำงานร่วมกับไลบรารี C คุณสามารถใช้สิ่งเหล่านี้ได้ ไม่ว่า C # จะมีคำหลักหรือไม่)
user541686

โอ้และปัญหาอื่น: แม้ว่าจะไม่พกพาทำไมพวกเขาจึงไม่ได้จัดทำเอกสารคำหลัก? อย่างน้อยที่สุดมันเป็นสิ่งจำเป็นเมื่อต้องสอดแทรก C varargs ดังนั้นอย่างน้อยพวกเขาก็พูดถึงมันได้
user541686

@ Mehrdad: อืมนั่นน่าสนใจ ฉันคิดว่าฉันมักจะสันนิษฐานว่าไฟล์ในโฟลเดอร์BCLของ. NET source นั้นเป็นส่วนหนึ่งของ BCL ไม่เคยให้ความสนใจกับส่วนมาตรฐานของ ECMA เลย มันค่อนข้างน่าเชื่อ ... ยกเว้นสิ่งเล็กน้อย: ไม่มีประโยชน์หรือไม่ที่จะรวมคุณลักษณะ (เป็นทางเลือก) ในข้อมูลจำเพาะ CLI หากไม่มีเอกสารเกี่ยวกับวิธีใช้งานที่ใดก็ตาม (มันจะสมเหตุสมผลถ้าTypedReferenceมีการบันทึกไว้สำหรับภาษาเดียว - พูด, จัดการ C ++ - แต่ถ้าไม่มีเอกสารภาษาและถ้าไม่มีใครสามารถใช้งานได้จริง ๆ แล้วทำไมถึงต้องกำหนดคุณสมบัติ?)
user541686

@ Mehrdad ฉันสงสัยว่าแรงจูงใจหลักคือความต้องการคุณลักษณะนี้ภายในสำหรับ interop ( เช่น [DllImport("...")] void Foo(__arglist); ) และพวกเขาใช้มันใน C # สำหรับการใช้งานของตัวเอง การออกแบบ CLI นั้นได้รับอิทธิพลจากหลายภาษา (คำอธิบายประกอบ "โครงสร้างพื้นฐานภาษาทั่วไปที่มีคำอธิบายประกอบ" แสดงให้เห็นถึงความจริงข้อนี้) การออกแบบรันไทม์ที่เหมาะสมสำหรับภาษามากที่สุดเท่าที่จะเป็นไปได้รวมถึงภาษาที่ไม่คาดฝัน ชื่อ) และนี่คือคุณลักษณะที่ตัวอย่างเช่นการใช้ C ที่ถูกจัดการตามสมมุติฐานอาจได้ประโยชน์จาก
Mehrdad Afshari

@Mehrdad: อ่า ... ใช่นั่นเป็นเหตุผลที่น่าเชื่อ ขอบคุณ!
user541686

15

ฉันไม่ใช่ Eric Lippert ดังนั้นฉันไม่สามารถพูดได้โดยตรงจากแรงจูงใจของ Microsoft แต่ถ้าฉันเดาเอาเองฉันก็จะพูดอย่างนั้น TypedReference et al ไม่ได้รับการบันทึกไว้อย่างดีเพราะตรงไปตรงมาคุณไม่ต้องการ

การใช้งานทุกอย่างที่คุณกล่าวถึงสำหรับคุณสมบัติเหล่านี้สามารถทำได้โดยไม่มีพวกเขาแม้ว่าจะมีการปรับประสิทธิภาพในบางกรณี แต่ C # (และ. NET โดยทั่วไป) ไม่ได้ออกแบบมาให้เป็นภาษาที่มีประสิทธิภาพสูง (ฉันเดาว่า "เร็วกว่า Java" เป็นเป้าหมายประสิทธิภาพ)

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

ยาสามัญซึ่งฉันจะบอกว่ามีประโยชน์หลักของความปลอดภัยประเภทยังปรับปรุงประสิทธิภาพการทำงานคล้ายกับTypedReferenceโดยการหลีกเลี่ยงการชกมวยและ unboxing อันที่จริงฉันสงสัยว่าทำไมคุณถึงชอบสิ่งนี้:

static void call(Action<int, TypedReference> action, TypedReference state){
    action(0, state);
}

สำหรับสิ่งนี้:

static void call<T>(Action<int, T> action, T state){
    action(0, state);
}

การแลกเปลี่ยนในขณะที่ฉันเห็นพวกเขาคืออดีตต้องการ JIT น้อยลง (และตามมาด้วยหน่วยความจำน้อยกว่า) ในขณะที่หลังมีความคุ้นเคยมากกว่าและฉันจะถือว่าเร็วขึ้นเล็กน้อย (โดยหลีกเลี่ยงการชี้ตัวชี้)

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


4
อืม ... "คุณไม่ต้องการพวกเขา" - ฉันน่าจะเห็นว่ากำลังมา :-) นั่นจริง แต่ก็ไม่จริง สิ่งที่คุณกำหนดเป็น "ต้องการ"? ยกตัวอย่างเช่นวิธีการต่อขยายจำเป็นอย่างยิ่งหรือไม่ เกี่ยวกับคำถามของคุณเกี่ยวกับการใช้ยาชื่อสามัญในcall(): เป็นเพราะรหัสไม่ติดกันเสมอ - ฉันอ้างถึงตัวอย่างที่มากกว่านั้นIAsyncResult.Stateซึ่งการแนะนำยาชื่อสามัญจะไม่เป็นไปได้เพราะในทันทีมันจะแนะนำยาชื่อสามัญสำหรับ ทุกคลาส / วิธีการที่เกี่ยวข้อง +1 สำหรับคำตอบแม้ว่า ... โดยเฉพาะอย่างยิ่งสำหรับการชี้ส่วน "เร็วกว่า Java" :]
user541686

1
โอ้และอีกจุดหนึ่ง: TypedReferenceอาจจะไม่ได้รับการเปลี่ยนแปลงเมื่อใดก็ตามในไม่ช้าเนื่องจากFieldInfo.SetValueDirectซึ่งเป็นสาธารณะและอาจใช้โดยนักพัฒนาบางคนขึ้นอยู่กับมัน :)
user541686

Ah แต่คุณทำวิธีการขยายความต้องการที่จะสนับสนุน LINQ อย่างไรก็ตามฉันไม่ได้พูดถึงความแตกต่างที่ดีที่มี / จำเป็นต้องมี ฉันจะไม่เรียกTypedReferenceอย่างใดอย่างหนึ่งเหล่านั้น (ไวยากรณ์ที่ชั่วร้ายและความไม่แน่นอนโดยรวมทำให้ฉันขาดคุณสมบัติในหมวดหมู่ที่ดี) ฉันพูดได้ว่ามันเป็นสิ่งที่ดีที่จะมีเมื่อคุณต้องการตัดแต่งไมโครวินาทีที่นี่และที่นั่น ที่กล่าวว่าฉันคิดถึงสถานที่สองแห่งในรหัสของตัวเองที่ฉันจะไปดูตอนนี้เพื่อดูว่าฉันสามารถเพิ่มประสิทธิภาพพวกเขาโดยใช้เทคนิคที่คุณชี้ให้เห็น
Paddy

1
@ Merhdad: ฉันทำงานกับวัตถุไบนารี serializer / deserializer ในเวลานั้นสำหรับการสื่อสารระหว่างกระบวนการ / interhost (TCP และไพพ์) เป้าหมายของฉันคือทำให้มีขนาดเล็ก (ในแง่ของจำนวนไบต์ที่ส่งผ่านสาย) และรวดเร็ว (ในแง่ของเวลาที่ใช้ในการทำให้เป็นอนุกรมและลดขนาด) เป็นไปได้ ฉันคิดว่าฉันอาจจะหลีกเลี่ยงมวยและ unboxing ด้วยTypedReferences แต่ IIRC, สถานที่เดียวที่ผมสามารถที่จะหลีกเลี่ยงมวยที่ไหนสักแห่งอยู่กับองค์ประกอบของอาร์เรย์เดียวมิติของวิทยาการ ประโยชน์ความเร็วเล็กน้อยที่นี่ไม่คุ้มกับความซับซ้อนที่เพิ่มเข้ากับโครงการทั้งหมดดังนั้นฉันจึงนำมันออกไป
Paddy

1
ได้รับdelegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);ชุดของประเภทTจะให้วิธีการแต่กระวนกระวายใจจะมีการสร้างรุ่นที่แตกต่างกันของวิธีการสำหรับประเภทค่าทุกActOnItem<TParam>(int index, ActByRef<T,TParam> proc, ref TParam param) TParamการใช้การอ้างอิงแบบพิมพ์จะทำให้วิธีการ JITted หนึ่งเวอร์ชันสามารถทำงานกับพารามิเตอร์ทุกประเภทได้
supercat

4

ฉันไม่สามารถระบุได้ว่าคำถามของคำถามนี้ควรจะประชดประชันหรือไม่: เป็นที่ยอมรับมานานแล้วว่าTypedReferenceเป็นลูกพี่ลูกน้องที่ช้าบวมและน่าเกลียดของตัวชี้ที่ได้รับการจัดการ 'จริง' ซึ่งเป็นสิ่งที่เราได้รับด้วยC ++ / CLI interior_ptr<T>หรือ แบบดั้งเดิมแม้โดยอ้างอิง ( ref/ out) พารามิเตอร์ในC # ในความเป็นจริงมันค่อนข้างยากที่จะทำให้TypedReferenceได้ประสิทธิภาพพื้นฐานเพียงแค่ใช้จำนวนเต็มเพื่อจัดทำดัชนีอาร์เรย์อาร์เรย์ CLR ดั้งเดิมใหม่ทุกครั้ง

รายละเอียดที่น่าเศร้าอยู่ที่นี่แต่โชคดีที่ไม่มีเรื่องนี้ในขณะนี้ ...

ตอนนี้คำถามนี้ได้ถูกนำเสนอโดย moot โดยผู้อ้างอิงใหม่และฟีเจอร์ส่งคืน refในC # 7

คุณสมบัติภาษาใหม่เหล่านี้ให้การสนับสนุนที่โดดเด่นและเป็นเลิศในC #สำหรับการประกาศการแบ่งปันและCLR การจัดการประเภทการอ้างอิงที่มีการจัดการที่แท้จริงในสถานการณ์ที่กำหนดล่วงหน้าอย่างระมัดระวัง

ข้อ จำกัด การใช้งานมีความเข้มงวดกว่าสิ่งที่ไม่ถูกต้องก่อนหน้านี้สำหรับTypedReference(และประสิทธิภาพการทำงานเป็นอักษรกระโดดจากที่เลวร้ายที่สุดที่ดีที่สุด ) ดังนั้นฉันไม่เห็นเหลือกรณีการใช้งานเท่าที่จะนึกออกในC #TypedReferenceสำหรับ ตัวอย่างเช่นก่อนหน้านี้ไม่มีวิธีที่จะคงอยู่TypedReferenceในGCheap ดังนั้นความเป็นจริงของพอยน์เตอร์ที่มีการจัดการที่เหนือกว่าในขณะนี้จึงไม่ใช่เรื่องง่าย

และเห็นได้ชัดว่าการตายของTypedReference- หรืออย่างน้อยก็เกือบเสร็จสิ้นอย่างน้อย - หมายถึงการทิ้ง__makerefขยะด้วยเช่นกัน

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