การเข้าถึง 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

  1. สร้าง Delegate: กำหนด delegate ที่ใช้รับวัตถุและส่งคืนวัตถุ ซึ่งจะช่วยให้เราสร้าง property getter ที่รวดเร็วได้

    public delegate object FastPropertyGetHandler(object target);
    
  2. สร้าง IL: โดยใช้ ILGenerator เราสามารถสร้างวิธีการ IL ที่โหลดวัตถุเป้าหมายเข้าสู่สแต็ค เรียกใช้ method getter และจัดการค่าคืนอย่างเหมาะสม

    private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type) {
        if (type.IsValueType) {
            ilGenerator.Emit(OpCodes.Box, type);
        }
    }
    
  3. รับ 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 ได้อย่างราบรื่น และช่วยให้โค้ดของคุณสะอาดและบำรุงรักษาง่ายขึ้น ขอให้โค้ดสนุก!