TL; DRเลื่อนไปที่ด้านล่างสุด
จากสิ่งที่ฉันเห็นคุณกำลังใช้ภาษาใหม่อยู่ด้านบนของ C # ดูเหมือนว่า enums จะแสดงถึงชนิดของตัวระบุ (หรือสิ่งใดก็ตามที่มีชื่อและที่ปรากฏในซอร์สโค้ดของภาษาใหม่) ซึ่งดูเหมือนว่าจะนำไปใช้กับโหนดที่จะถูกเพิ่มลงในการแสดงแผนผังของโปรแกรม
ในสถานการณ์เฉพาะนี้มีพฤติกรรม polymorphic น้อยมากระหว่างโหนดประเภทต่าง ๆ กล่าวอีกนัยหนึ่งในขณะที่มันจำเป็นสำหรับต้นไม้ที่จะสามารถบรรจุโหนดที่แตกต่างกันมาก (พันธุ์), การเยี่ยมชมที่แท้จริงของโหนดเหล่านี้โดยทั่วไปจะหันไปใช้โซ่ยักษ์ถ้า - แล้ว - อื่นโซ่ (หรือinstanceof
/ is
ตรวจสอบ) เช็คขนาดยักษ์เหล่านี้จะเกิดขึ้นในหลาย ๆ ที่ทั่วทั้งโครงการ นี่คือเหตุผลที่ enums อาจดูเหมือนว่ามีประโยชน์หรืออย่างน้อยก็มีประโยชน์เหมือนกับinstanceof
/ is
เช็ค
รูปแบบของผู้เข้าชมอาจยังมีประโยชน์ ในคำอื่น ๆ instanceof
ที่มีรูปแบบการเขียนโปรแกรมต่างๆที่สามารถนำมาใช้แทนของห่วงโซ่ยักษ์ใหญ่ของ อย่างไรก็ตามหากคุณต้องการพูดคุยเกี่ยวกับประโยชน์และข้อเสียต่าง ๆ คุณจะต้องแสดงตัวอย่างรหัสจากห่วงโซ่ที่น่าเกลียดที่สุดของinstanceof
โครงการแทนที่จะเป็นการพูดคลุมเครือเกี่ยวกับ enums
นี่ไม่ได้เป็นการบอกว่าคลาสและลำดับชั้นการสืบทอดไม่เป็นประโยชน์ ค่อนข้างตรงกันข้าม ในขณะที่ไม่มีพฤติกรรม polymorphic ใด ๆ ที่ทำงานข้ามการประกาศทุกประเภท (นอกเหนือจากข้อเท็จจริงที่ว่าการประกาศทุกครั้งจะต้องมีName
คุณสมบัติ) แต่ก็มีพฤติกรรม polymorphic มากมายที่แบ่งปันโดยพี่น้องในบริเวณใกล้เคียง ตัวอย่างเช่นFunction
และProcedure
อาจแบ่งปันพฤติกรรมบางอย่าง (ทั้งเรียกได้และยอมรับรายการของอาร์กิวเมนต์ที่พิมพ์) และPropertyGet
จะสืบทอดพฤติกรรมจากFunction
(ทั้งสองมีReturnType
) คุณอาจใช้ enums หรือการตรวจสอบการสืบทอดสำหรับห่วงโซ่ if-then-else ยักษ์ แต่พฤติกรรม polymorphic แต่การแยกส่วนจะยังคงต้องดำเนินการในชั้นเรียน
มีคำแนะนำออนไลน์จำนวนมากกับมากเกินไปของคุณinstanceof
/ is
ตรวจสอบ ประสิทธิภาพไม่ได้เป็นหนึ่งในเหตุผล เหตุผลก็คือเพื่อป้องกันโปรแกรมเมอร์จากการค้นพบพฤติกรรม polymorphic ที่เหมาะสมอย่างเป็นธรรมชาติเช่นถ้าinstanceof
/ is
เป็นไม้ยันรักแร้ แต่ในสถานการณ์ของคุณคุณไม่มีทางเลือกอื่นเนื่องจากโหนดเหล่านี้มีเหมือนกันน้อยมาก
ตอนนี้นี่คือคำแนะนำที่เป็นรูปธรรม
มีหลายวิธีในการแสดงการจัดกลุ่มที่ไม่ใช่ใบไม้
เปรียบเทียบส่วนที่ตัดตอนมาของรหัสต้นฉบับของคุณ ...
[Flags]
public enum DeclarationType
{
Member = 1 << 7,
Procedure = 1 << 8 | Member,
Function = 1 << 9 | Member,
Property = 1 << 10 | Member,
PropertyGet = 1 << 11 | Property | Function,
PropertyLet = 1 << 12 | Property | Procedure,
PropertySet = 1 << 13 | Property | Procedure,
LibraryFunction = 1 << 23 | Function,
LibraryProcedure = 1 << 24 | Procedure,
}
เป็นเวอร์ชั่นที่มีการปรับเปลี่ยนนี้:
[Flags]
public enum DeclarationType
{
Nothing = 0, // to facilitate bit testing
// Let's assume Member is not a concrete thing,
// which means it doesn't need its own bit
/* Member = 1 << 7, */
// Procedure and Function are concrete things; meanwhile
// they can still have sub-types.
Procedure = 1 << 8,
Function = 1 << 9,
Property = 1 << 10,
PropertyGet = 1 << 11,
PropertyLet = 1 << 12,
PropertySet = 1 << 13,
LibraryFunction = 1 << 23,
LibraryProcedure = 1 << 24,
// new
Procedures = Procedure | PropertyLet | PropertySet | LibraryProcedure,
Functions = Function | PropertyGet | LibraryFunction,
Properties = PropertyGet | PropertyLet | PropertySet,
Members = Procedures | Functions | Properties,
LibraryMembers = LibraryFunction | LibraryProcedure
}
เวอร์ชันที่แก้ไขนี้จะหลีกเลี่ยงการจัดสรรบิตไปยังประเภทการประกาศที่ไม่เป็นรูปธรรม แทนประเภทการประกาศที่ไม่เป็นรูปธรรม (การจัดกลุ่มนามธรรมของประเภทการประกาศ) เพียงแค่มีค่า enum ซึ่งเป็นบิตหรือ - (สหภาพของบิต) ในเด็กทุกคน
มีข้อแม้: ถ้ามีประเภทการประกาศนามธรรมที่มีลูกคนเดียวและหากมีความต้องการที่จะแยกแยะความแตกต่างระหว่างนามธรรมหนึ่ง (ผู้ปกครอง) จากคอนกรีตหนึ่ง (เด็ก) จากนั้นหนึ่งนามธรรมจะยังคงต้องการบิตของตัวเอง .
หนึ่งข้อแม้ที่เฉพาะเจาะจงสำหรับคำถามนี้: a Property
เริ่มต้นเป็นตัวระบุ (เมื่อคุณเพิ่งเห็นชื่อโดยไม่เห็นว่ามันถูกใช้ในรหัส) แต่มันอาจแปลงร่างเป็นPropertyGet
/ PropertyLet
/ PropertySet
ทันทีที่คุณเห็นว่ามันถูกใช้อย่างไร ในรหัส กล่าวอีกนัยหนึ่งในขั้นตอนการแยกวิเคราะห์ที่แตกต่างกันคุณอาจจำเป็นต้องทำเครื่องหมายProperty
ตัวระบุว่าเป็น "ชื่อนี้หมายถึงสถานที่ให้บริการ" และในภายหลังเปลี่ยนเป็น "บรรทัดของรหัสนี้คือการเข้าถึงคุณสมบัตินี้ด้วยวิธีใดวิธีหนึ่ง"
ในการแก้ไขข้อแม้นี้คุณอาจต้องใช้ enums สองชุด หนึ่ง enum หมายถึงสิ่งที่ชื่อ (ตัวระบุ) คือ; อีก enum หมายถึงสิ่งที่รหัสพยายามทำ (เช่นการประกาศเนื้อความของบางสิ่งบางอย่างพยายามใช้บางอย่างในวิธีที่แน่นอน)
พิจารณาว่าข้อมูลเสริมเกี่ยวกับแต่ละค่า enum สามารถอ่านได้จากอาเรย์แทนหรือไม่
คำแนะนำนี้ไม่สามารถเกิดขึ้นพร้อมกันกับคำแนะนำอื่น ๆ ได้เนื่องจากต้องการการแปลงค่า powers-of-two กลับไปเป็นค่าจำนวนเต็มแบบไม่ลบ
public enum DeclarationType
{
Procedure = 8,
Function = 9,
Property = 10,
PropertyGet = 11,
PropertyLet = 12,
PropertySet = 13,
LibraryFunction = 23,
LibraryProcedure = 24,
}
static readonly bool[] DeclarationTypeIsMember = new bool[32]
{
?, ?, ?, ?, ?, ?, ?, ?, // bit[0] ... bit[7]
true, true, true, true, true, true, ?, ?, // bit[8] ... bit[15]
?, ?, ?, ?, ?, ?, ?, true, // bit[16] ... bit[23]
true, ... // bit[24] ...
}
static bool IsMember(DeclarationType dt)
{
int intValue = (int)dt;
return (intValue < 0 || intValue >= 32) ? false : DeclarationTypeIsMember[intValue];
// you can also throw an exception if the enum is outside range.
}
// likewise for IsFunction(dt), IsProcedure(dt), IsProperty(dt), ...
การบำรุงรักษาจะเป็นปัญหาได้
ตรวจสอบว่าการแมปแบบหนึ่งต่อหนึ่งระหว่างประเภท C # (คลาสในลำดับชั้นการสืบทอด) และค่า enum ของคุณ
(หรืออีกวิธีหนึ่งคุณสามารถปรับค่า enum ของคุณเพื่อให้แน่ใจว่าการแมปแบบหนึ่งต่อหนึ่งพร้อมกับประเภท)
ใน C # ห้องสมุดจำนวนมากละเมิดType object.GetType()
วิธีการที่ดีสำหรับดีหรือไม่ดี
ทุกที่ที่คุณเก็บค่า enum เป็นค่าคุณอาจถามตัวเองว่าคุณสามารถเก็บType
ค่าเป็นค่าได้หรือไม่
ในการใช้เคล็ดลับนี้คุณสามารถเริ่มต้นตารางแฮชแบบอ่านอย่างเดียวสองตาราง ได้แก่ :
// For disambiguation, I'll assume that the actual
// (behavior-implementing) classes are under the
// "Lang" namespace.
static readonly Dictionary<Type, DeclarationType> TypeToDeclEnum = ...
{
{ typeof(Lang.Procedure), DeclarationType.Procedure },
{ typeof(Lang.Function), DeclarationType.Function },
{ typeof(Lang.Property), DeclarationType.Property },
...
};
static readonly Dictionary<DeclarationType, Type> DeclEnumToType = ...
{
// same as the first dictionary;
// just swap the key and the value
...
};
การป้องกันขั้นสุดท้ายสำหรับผู้ที่แนะนำคลาสและลำดับชั้นการสืบทอด ...
เมื่อคุณเห็นว่าenums เป็นการประมาณลำดับชั้นการสืบทอดคำแนะนำต่อไปนี้จะเก็บ:
- ออกแบบ (หรือปรับปรุง) ลำดับชั้นการสืบทอดของคุณก่อน
- จากนั้นย้อนกลับและออกแบบ enums ของคุณเพื่อประมาณลำดับชั้นการสืบทอดนั้น
DeclarationType
อย่างไร ถ้าผมต้องการที่จะกำหนดหรือไม่x
เป็นชนิดย่อยของy
ฉันอาจจะต้องการที่จะเขียนว่าไม่เป็นx.IsSubtypeOf(y)
x && y == y