การเข้าถึง Custom Attributes บนค่า Enum ใน C# อย่างมีประสิทธิภาพ
หากคุณกำลังพัฒนาแอพพลิเคชันใน C# คุณอาจพบความจำเป็นในการดึง custom attributes ที่เกี่ยวข้องกับค่า enum บางครั้ง การทำงานนี้อาจดูเรียบง่ายโดยเฉพาะอย่างยิ่งสำหรับผู้ที่ไม่คุ้นเคยกับ reflection และการสร้าง IL code อย่างไรก็ตาม การแปลงค่า enum เป็นสายอักษร (ชื่อของมัน) สามารถทำให้เกิดปัญหาเกี่ยวกับประสิทธิภาพได้ ในบล็อกโพสต์นี้ เราจะสำรวจวิธีการที่สะอาดและมีประสิทธิภาพในการดึง attributes เหล่านี้โดยไม่ต้องเสียเวลาในการแปลงสายอักษร
ทำความเข้าใจกับปัญหา
เริ่มต้นด้วยตัวอย่างอย่างรวดเร็วที่เรากำหนด enum ที่มี custom attributes ประดับ:
public enum MyEnum {
[CustomInfo("นี่คือ custom attrib")]
None = 0,
[CustomInfo("นี่คือ attrib อีกหนึ่งตัว")]
ValueA,
[CustomInfo("นี่มีธงเพิ่มเติม", AllowSomething = true)]
ValueB,
}
ในโค้ดนี้ เรามี enum ชื่อ MyEnum
ซึ่งประกอบด้วยค่าหลายค่า โดยแต่ละค่ามี CustomInfo
attribute ที่ให้รายละเอียดข้อมูลเพิ่มเติมเกี่ยวกับสมาชิก enum แต่ละตัว
ความท้าทาย
ความท้าทายหลักเกิดขึ้นเมื่อเราต้องดึง CustomInfo
attributes เหล่านี้จากอินสแตนซ์ของ enum วิธีการเริ่มต้นที่นึกถึงคือการใช้โค้ดต่อไปนี้:
public CustomInfoAttribute GetInfo(MyEnum enumInput) {
Type typeOfEnum = enumInput.GetType();
FieldInfo fi = typeOfEnum.GetField(enumInput.ToString());
return fi.GetCustomAttributes(typeof(CustomInfoAttribute), false).
FirstOrDefault() as CustomInfoAttribute;
}
ในขณะที่ฟังก์ชันนี้ดึง attribute ได้อย่างมีประสิทธิภาพ แต่มันมีปัญหาเกี่ยวกับประสิทธิภาพเนื่องจากการใช้ enumInput.ToString()
ซึ่งแปลงค่า enum เป็นการแทนที่เป็นสายอักษร
วิธีที่ดีกว่า: การใช้ IL Code Generation
โชคดีที่มีวิธีการที่มีประสิทธิภาพมากขึ้นในการเข้าถึง custom attributes โดยไม่ต้องแปลง enums เป็นสายอักษร โดยการใช้การสร้าง IL code ด้วยวิธีการที่มีพลศาสตร์และ ILGenerator
เราสามารถลดค่าใช้จ่ายและปรับปรุงประสิทธิภาพได้ ด้านล่างนี้ เราจะบรรยายวิธีการสร้าง delegate ที่ช่วยในการเข้าถึง properties ได้อย่างรวดเร็ว ซึ่งสามารถปรับใช้ในการดึง attributes ได้เช่นกัน
ขั้นตอนในการใช้งาน IL Code Generation
-
สร้าง Delegate: กำหนด delegate ที่ใช้รับวัตถุและส่งคืนวัตถุ ซึ่งจะช่วยให้เราสร้าง property getter ที่รวดเร็วได้
public delegate object FastPropertyGetHandler(object target);
-
สร้าง IL: โดยใช้
ILGenerator
เราสามารถสร้างวิธีการ IL ที่โหลดวัตถุเป้าหมายเข้าสู่สแต็ค เรียกใช้ method getter และจัดการค่าคืนอย่างเหมาะสมprivate static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type) { if (type.IsValueType) { ilGenerator.Emit(OpCodes.Box, type); } }
-
รับ Property Getter: ตอนนี้เราสร้างวิธีในการสร้างวิธีพลศาสตร์และสร้าง IL code ที่จำเป็นในการสร้าง property getter ที่รวดเร็ว:
public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo) { DynamicMethod dynamicMethod = new DynamicMethod( string.Empty, typeof(object), new Type[] { typeof(object) }, propInfo.DeclaringType.Module); ILGenerator ilGenerator = dynamicMethod.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null); EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType); ilGenerator.Emit(OpCodes.Ret); FastPropertyGetHandler getter = (FastPropertyGetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler)); return getter; }
สรุป
ด้วยการดำเนินการตามวิธีการที่กล่าวถึงข้างต้น คุณสามารถลดค่าใช้จ่ายที่เกี่ยวข้องกับการดึง attributes จากค่า enum ได้อย่างมีประสิทธิภาพ วิธีการนี้ไม่เพียงแต่จะกำจัดการแปลงสายอักษรที่ไม่จำเป็น ยังช่วยให้มีประสิทธิภาพใน runtime ได้ดียิ่งขึ้น ซึ่งเป็นสิ่งสำคัญสำหรับแอพพลิเคชั่นที่ต้องการความมีประสิทธิภาพ
การนำเทคนิคเหล่านี้เข้ามาใช้ในโปรเจ็ค C# ของคุณจะช่วยให้คุณจัดการกับ attributes ของ enum ได้อย่างราบรื่น และช่วยให้โค้ดของคุณสะอาดและบำรุงรักษาง่ายขึ้น ขอให้โค้ดสนุก!