“ ชั้นเรียนพิเศษ” คืออะไร?


114

หลังจากล้มเหลวในการรวบรวมสิ่งต่อไปนี้:

public class Gen<T> where T : System.Array
{
}

ด้วยข้อผิดพลาด

ข้อ จำกัด ไม่สามารถเป็นคลาสพิเศษได้ `` System.Array ''

ผมเริ่มสงสัยว่าสิ่งที่เป็น "การเรียนพิเศษ"?

ผู้คนมักจะได้รับข้อผิดพลาดประเภทเดียวกันเมื่อพวกเขาระบุSystem.Enumในข้อ จำกัด ทั่วไป ผมได้รับผลเช่นเดียวกันกับSystem.Object, System.Delegate, System.MulticastDelegateและSystem.ValueTypeมากเกินไป

มีมากกว่านี้ไหม ฉันไม่พบข้อมูลใด ๆ เกี่ยวกับ "ชั้นเรียนพิเศษ" ใน C #

นอกจากนี้สิ่งที่เป็นพิเศษเกี่ยวกับการเรียนที่เราไม่สามารถใช้พวกเขาเป็นข้อ จำกัด ประเภททั่วไป?


14
ฉันไม่คิดว่านี่เป็นการทำซ้ำโดยตรง คำถามไม่ใช่ "ทำไมฉันใช้สิ่งนี้เป็นข้อ จำกัด ไม่ได้" มันคือ "คลาสพิเศษเหล่านี้คืออะไร" ฉันได้ดูคำถามเหล่านั้นแล้วและพวกเขาเพียง แต่ระบุว่าทำไมจึงไม่มีประโยชน์ที่จะใช้เป็นข้อ จำกัด โดยไม่ได้อธิบายว่า "คลาสพิเศษ" คืออะไรและทำไมจึงถูกพิจารณาว่าพิเศษ
Adam Houldsworth

2
จากประสบการณ์ของฉันคลาสที่ใช้ แต่คุณไม่สามารถใช้โดยตรงได้โดยปริยายผ่านไวยากรณ์อื่น ๆ เท่านั้นคือคลาสพิเศษ Enum จัดอยู่ในประเภทเดียวกัน อะไรทำให้พวกเขาพิเศษกันแน่ฉันไม่รู้
Lasse V.Karlsen

@AndyKorneyev: คำถามนั้นแตกต่างกัน ฉันกำลังขอคำจำกัดความของ "คลาสพิเศษ" และ / หรือรายการที่ครอบคลุมของสิ่งเหล่านี้ คำถามนั้นถามถึงเหตุผล System.Array ไม่สามารถเป็นข้อ จำกัด ประเภททั่วไปได้
Mints97

จากเอกสารดังกล่าวระบุว่า "[... ] เฉพาะระบบและคอมไพเลอร์เท่านั้นที่สามารถได้มาจากคลาส Array อย่างชัดเจน" เป็นไปได้ว่านี่คือสิ่งที่ทำให้เป็นคลาสพิเศษ - คอมไพเลอร์ได้รับการปฏิบัติเป็นพิเศษ
RB.

1
@RB: ผิด ตรรกะนี้จะหมายถึงSystem.Objectเป็นไม่ได้เป็น "เรียนพิเศษ" เช่นนี้เป็นที่ถูกต้อง: public class X : System.Object { }แต่System.Objectก็ยังคงเป็น "ชั้นพิเศษ"
Mints97

คำตอบ:


106

จากซอร์สโค้ด Roslyn ดูเหมือนรายการประเภทฮาร์ดโค้ด:

switch (type.SpecialType)
{
    case SpecialType.System_Object:
    case SpecialType.System_ValueType:
    case SpecialType.System_Enum:
    case SpecialType.System_Delegate:
    case SpecialType.System_MulticastDelegate:
    case SpecialType.System_Array:
        // "Constraint cannot be special class '{0}'"
        Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type);
        return false;
}

ที่มา: Binder_Constraints.cs IsValidConstraintType
ฉันพบโดยใช้การค้นหา GitHub: "ข้อ จำกัด ไม่สามารถเป็นคลาสพิเศษได้"


1
@kobi 702 กลายเป็นข้อผิดพลาดของคอมไพเลอร์ CS0702 ดังที่เห็นในเอาต์พุตของคอมไพเลอร์ (ซึ่งคำถามนี้ถูกละเลยที่จะอ้างถึง) และคำตอบอื่น ๆ
AakashM

1
@AakashM - ขอบคุณ! ฉันพยายามรวบรวมและไม่ได้รับหมายเลขข้อผิดพลาดด้วยเหตุผลบางประการ จากนั้นฉันใช้เวลาเกือบ 5 นาทีในการค้นหาและไม่มีเวลามากพอที่จะแก้ไขความคิดเห็นของฉัน เรื่องเศร้า.
Kobi

1
@Kobi: คุณต้องมองไปที่การส่งออก -window CS0702มีคุณค้นหาหมายเลขรหัสข้อผิดพลาดคอมไพเลอร์ที่แน่นอน
Tim Schmelter

9
ตอนนี้คำถามที่แท้จริงคือทำไมถึงเรียนพิเศษ?
David กล่าวว่า Reinstate Monica

@DavidGrinberg บางทีเหตุผลก็คือคุณไม่สามารถสืบทอดจากประเภทเหล่านี้ได้โดยตรง (ยกเว้นobject) หรืออย่างน้อยก็มีส่วนเกี่ยวข้องกับมัน นอกจากนี้ยังwhere T : Arrayอนุญาตให้ผ่าน Assay เป็น T ซึ่งอาจไม่ใช่สิ่งที่คนส่วนใหญ่ต้องการ
IllidanS4 ต้องการให้ Monica กลับมาใน

42

ฉันพบความคิดเห็นของ Jon Skeet จากปี 2008 ในคำถามที่คล้ายกัน: เหตุใดจึงไม่รองรับSystem.Enumข้อ จำกัด

ฉันรู้ว่านี่เป็นหัวข้อเล็กน้อยแต่เขาถาม Eric Lippert (ทีม C #) เกี่ยวกับเรื่องนี้และพวกเขาให้คำตอบนี้:

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

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

อย่างไรก็ตามเนื่องจากข้อ จำกัด ด้านการตั้งเวลาเราอาจไม่สามารถนำคุณลักษณะเหล่านี้ไปใช้ในภาษาเวอร์ชันถัดไปได้


10
@YuvalItzchakov - การอ้าง Github \ MSDN ดีกว่าไหม ทีม C # ได้ให้คำตอบที่เป็นรูปธรรมเกี่ยวกับปัญหาหรือคำถามที่คล้ายกัน .. มันไม่สามารถทำร้ายใครได้จริงๆ Jon Skeet เพิ่งอ้างถึงพวกเขาและค่อนข้างน่าเชื่อถือเมื่อถึง C # ..
Amir Popovich

5
ไม่ต้องมาอารมณ์เสีย ฉันไม่ได้หมายความว่านี่ไม่ใช่คำตอบที่ถูกต้อง :) เป็นเพียงการแบ่งปันความคิดของฉันเกี่ยวกับรากฐานที่เป็น jonskeet p
Yuval Itzchakov

40
FYI BTW ฉันคิดว่านั่นคือฉันที่คุณกำลังอ้างถึงที่นั่น :-)
Eric Lippert

2
@EricLippert - นั่นทำให้คำพูดน่าเชื่อถือมากยิ่งขึ้น
Amir Popovich

โดเมนของลิงค์ในคำตอบตายแล้ว
ปาง

25

ตามMSDNเป็นรายการคลาสคงที่:

ข้อผิดพลาดของคอมไพเลอร์ CS0702

ข้อ จำกัด ไม่สามารถเป็น 'ตัวระบุ' คลาสพิเศษประเภทต่อไปนี้ไม่สามารถใช้เป็นข้อ จำกัด :

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType

4
เท่ดูเหมือนคำตอบที่ใช่หาดี! แต่อยู่ที่ไหนSystem.MulticastDelegateในรายการ?
Mints97

8
@ Mints97: ไม่รู้อาจขาดเอกสาร?
Tim Schmelter

ดูเหมือนว่าคุณจะไม่สามารถสืบทอดจากคลาสเหล่านี้ได้
David Klempfner

14

ตามข้อกำหนดภาษา C # 4.0 (รหัส: [10.1.5] ข้อ จำกัด พารามิเตอร์ประเภท) บอกสองสิ่ง:

1] ประเภทต้องไม่ใช่วัตถุ เนื่องจากทุกประเภทมาจากวัตถุข้อ จำกัด ดังกล่าวจะไม่มีผลหากได้รับอนุญาต

2] ถ้า T ไม่มีข้อ จำกัด หลักหรือข้อ จำกัด ประเภทพารามิเตอร์คลาสพื้นฐานที่มีประสิทธิผลคือออบเจ็กต์

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

public class Gen<T> where T : class
{
}

สิ่งนี้จะห้ามไม่ให้ประเภททั่วไปเป็นประเภทค่าเช่น int หรือโครงสร้างเป็นต้น

นอกจากนี้ Constraint ไม่สามารถเป็น 'ตัวระบุ' คลาสพิเศษประเภทต่อไปนี้ไม่สามารถใช้เป็นข้อ จำกัด :

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType

12

มีชั้นเรียนบางอย่างในกรอบที่มีประสิทธิภาพผ่านในลักษณะพิเศษเพื่อทุกประเภทที่ได้มาจากพวกเขาแต่ไม่ได้มีลักษณะเหล่านั้นตัวเอง CLR เองไม่ได้กำหนดข้อห้ามในการใช้คลาสเหล่านั้นเป็นข้อ จำกัด แต่ประเภททั่วไปที่ จำกัด ไว้สำหรับพวกเขาจะไม่ได้รับลักษณะที่ไม่ได้รับการสืบทอดมาอย่างที่ประเภทคอนกรีตจะทำได้ ผู้สร้าง C # ตัดสินใจว่าเนื่องจากพฤติกรรมดังกล่าวอาจทำให้บางคนสับสนและพวกเขาไม่เห็นประโยชน์ใด ๆ กับมันพวกเขาจึงควรห้ามข้อ จำกัด ดังกล่าวแทนที่จะอนุญาตให้พวกเขาประพฤติตามที่พวกเขาทำใน CLR

ตัวอย่างเช่นหากมีคนหนึ่งได้รับอนุญาตให้เขียน: void CopyArray<T>(T dest, T source, int start, int count); หนึ่งจะสามารถส่งผ่านdestและsourceวิธีการที่คาดว่าจะมีอาร์กิวเมนต์ประเภทSystem.Array; อีกคนหนึ่งจะได้รับการตรวจสอบรวบรวมเวลานั้นdestและsourceเป็นชนิดอาร์เรย์ที่เข้ากันได้ แต่จะไม่สามารถที่จะเข้าถึงองค์ประกอบของอาร์เรย์ที่ใช้[]ประกอบการ

การไม่สามารถใช้Arrayเป็นข้อ จำกัด นั้นค่อนข้างง่ายในการแก้ไขเนื่องจากvoid CopyArray<T>(T[] dest, T[] source, int start, int count)จะใช้ได้ในเกือบทุกสถานการณ์ที่วิธีการเดิมจะใช้ได้ผล อย่างไรก็ตามมันมีจุดอ่อน: วิธีการเดิมจะใช้ได้ในสถานการณ์ที่อาร์กิวเมนต์หนึ่งหรือทั้งสองเป็นประเภทSystem.Arrayในขณะที่ปฏิเสธกรณีที่อาร์กิวเมนต์เป็นประเภทอาร์เรย์ที่เข้ากันไม่ได้ การเพิ่มการโอเวอร์โหลดโดยที่อาร์กิวเมนต์ทั้งสองเป็นประเภทSystem.Arrayจะทำให้โค้ดยอมรับกรณีเพิ่มเติมที่ควรยอมรับ แต่ยังทำให้ยอมรับกรณีที่ไม่ควรผิดพลาดด้วย

ฉันพบว่าการตัดสินใจที่จะทำผิดกฎหมายข้อ จำกัด พิเศษส่วนใหญ่นั้นน่ารำคาญ สิ่งเดียวที่จะไม่มีความหมายทางความหมายก็คือSystem.Object[เนื่องจากหากเป็นข้อ จำกัด ทางกฎหมายสิ่งใดก็จะตอบสนองได้] System.ValueTypeอาจไม่มีประโยชน์มากนักเนื่องจากการอ้างอิงประเภทValueTypeไม่ได้มีความเหมือนกันกับประเภทค่ามากนัก แต่อาจมีค่าบางอย่างในกรณีที่เกี่ยวข้องกับการสะท้อนกลับ ทั้งสองอย่างSystem.EnumและSystem.Delegateจะมีการใช้งานจริง แต่เนื่องจากผู้สร้าง C # ไม่ได้คิดถึงสิ่งเหล่านี้พวกเขาจึงผิดกฎหมายโดยไม่มีเหตุผลที่ดี


10

สิ่งต่อไปนี้สามารถพบได้ใน CLR ผ่าน C # 4th Edition:

ข้อ จำกัด หลัก

พารามิเตอร์ชนิดสามารถระบุข้อ จำกัด หลักเป็นศูนย์หรือข้อ จำกัด หลักหนึ่งข้อ ข้อ จำกัด หลักอาจเป็นชนิดอ้างอิงที่ระบุคลาสที่ไม่ได้ปิดผนึก คุณไม่สามารถระบุหนึ่งในประเภทที่อ้างอิงพิเศษต่อไปนี้: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.EnumหรือSystem.Void เมื่อระบุข้อ จำกัด ประเภทการอ้างอิงคุณสัญญากับคอมไพเลอร์ว่าอาร์กิวเมนต์ประเภทที่ระบุจะเป็นประเภทเดียวกันหรือประเภทที่ได้มาจากประเภทข้อ จำกัด


ดูเพิ่มเติม: C # LS ส่วน 10.1.4.1: คลาสพื้นฐานโดยตรงของประเภทชั้นเรียนต้องไม่เป็นประเภทใดประเภทหนึ่งต่อไปนี้: System.Array, System.Delegate, System.MulticastDelegate, หรือSystem.Enum System.ValueTypeนอกจากนี้การประกาศคลาสทั่วไปไม่สามารถใช้System.Attributeเป็นคลาสพื้นฐานโดยตรงหรือโดยอ้อม
Jeroen Vannevel

5

ฉันไม่คิดว่าจะมีคำจำกัดความอย่างเป็นทางการของ "คลาสพิเศษ" / "ประเภทพิเศษ"

คุณอาจนึกถึงพวกมัน aa ซึ่งใช้กับความหมายของประเภท "ปกติ" ไม่ได้:

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

ปล. ฉันจะเพิ่มSystem.Voidในรายการ


2
System.Voidให้ข้อผิดพลาดที่แตกต่างกันอย่างสิ้นเชิงเมื่อใช้เป็นข้อ จำกัด ทั่วไป =)
Mints97

@ Mints97: จริง. แต่ถ้าคำถามเกี่ยวกับ "พิเศษ" ก็ใช่ว่าvoidจะพิเศษมาก :)
Dennis

@ เดนนิส: รหัสที่มีพารามิเตอร์สองสามประเภทที่ จำกัด ให้System.Arrayสามารถใช้วิธีการเช่นArray.Copyย้ายข้อมูลจากที่หนึ่งไปยังอีกที่หนึ่ง รหัสกับพารามิเตอร์ของประเภทที่ จำกัดSystem.Delegateจะสามารถที่จะใช้Delegate.Combineกับพวกเขาและโยนผลให้ชนิดที่เหมาะสม การใช้ประเภททั่วไปที่รู้จักกันอย่างมีประสิทธิภาพEnumจะใช้การสะท้อนกลับหนึ่งครั้งสำหรับแต่ละประเภทดังกล่าว แต่HasAnyFlagวิธีการทั่วไปอาจเร็วกว่าวิธีที่ไม่ใช่ทั่วไปถึง 10 เท่า
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.