ฉันจะได้รับค่าคงประเภททั้งหมดโดยการสะท้อน?


คำตอบ:


265

แม้ว่าจะเป็นรหัสเก่า:

private FieldInfo[] GetConstants(System.Type type)
{
    ArrayList constants = new ArrayList();

    FieldInfo[] fieldInfos = type.GetFields(
        // Gets all public and static fields

        BindingFlags.Public | BindingFlags.Static | 
        // This tells it to get the fields from all base types as well

        BindingFlags.FlattenHierarchy);

    // Go through the list and only pick out the constants
    foreach(FieldInfo fi in fieldInfos)
        // IsLiteral determines if its value is written at 
        //   compile time and not changeable
        // IsInitOnly determines if the field can be set 
        //   in the body of the constructor
        // for C# a field which is readonly keyword would have both true 
        //   but a const field would have only IsLiteral equal to true
        if(fi.IsLiteral && !fi.IsInitOnly)
            constants.Add(fi);           

    // Return an array of FieldInfos
    return (FieldInfo[])constants.ToArray(typeof(FieldInfo));
}

แหล่ง

คุณสามารถแปลงเป็นโค้ดที่สะอาดกว่าโดยใช้ generics และ LINQ:

private List<FieldInfo> GetConstants(Type type)
{
    FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public |
         BindingFlags.Static | BindingFlags.FlattenHierarchy);

    return fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
}

หรือมีหนึ่งบรรทัด:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();

13
+1 ของฉันคือก่อนที่ฉันจะผ่านบรรทัดที่ 2 .. ฉันสังเกตว่าคุณกำลังผ่านทุกขั้นตอนด้วย ... วัตถุประสงค์ที่ตั้งใจออกแบบโดย ... ! นี้อยู่ดังนั้นสิ่งสำคัญเมื่อหนึ่งในความต้องการที่จะเรียนรู้จากมัน ฉันหวังว่าทุกคนที่มีประสบการณ์ของคุณจะทำอย่างที่คุณทำที่นี่
LoneXcoder

4
ฉันไม่แน่ใจเกี่ยวกับการยืนยันเกี่ยวกับ IsLiteral และ IsInitOnly ในการทดสอบดูเหมือนว่าคุณสมบัติแบบอ่านอย่างเดียว IsLiteral เป็นเท็จเสมอดังนั้น IsLiteral จึงเป็นธงเดียวที่คุณต้องตรวจสอบเพื่อหาค่าคงที่และคุณสามารถเพิกเฉย IsInitOnly ได้ ฉันลองใช้กับฟิลด์ประเภทอื่น (เช่น String, Int32) เพื่อดูว่าสิ่งนี้สร้างความแตกต่าง แต่ไม่ได้
Mark Watts

49
นอกจากนี้ในการรับค่าของ const จาก FieldInfo ให้ใช้ GetRawConstantValue ()
Sam Sippe

@ MarkWatts ถูกต้อง อาจมีการเปลี่ยนแปลงพฤติกรรมนับตั้งแต่มีการโพสต์สิ่งนี้ ในกรณีใด ๆ เอกสารของการIsLiteralพูดif its value is written at compile timeและที่เป็นจริงเฉพาะสำหรับค่าคงที่ซึ่งเป็นวิธีการทำงานตอนนี้ (ทดสอบ ณ . NET 4.5.2)
nawfal

52

หากคุณต้องการได้รับค่าคงที่ทุกประเภทเฉพาะจากประเภทเป้าหมายนี่คือวิธีการขยาย (ขยายคำตอบบางส่วนในหน้านี้):

public static class TypeUtilities
{
    public static List<T> GetAllPublicConstantValues<T>(this Type type)
    {
        return type
            .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
            .Where(fi => fi.IsLiteral && !fi.IsInitOnly && fi.FieldType == typeof(T))
            .Select(x => (T)x.GetRawConstantValue())
            .ToList();
    }
}

สำหรับชั้นเรียนเช่นนี้

static class MyFruitKeys
{
    public const string Apple = "apple";
    public const string Plum = "plum";
    public const string Peach = "peach";
    public const int WillNotBeIncluded = -1;
}

คุณสามารถรับstringค่าคงที่เช่นนี้:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"

ทำไมไม่นี้.Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();?
T-moty

17

เป็นส่วนขยายประเภท:

public static class TypeExtensions
{
    public static IEnumerable<FieldInfo> GetConstants(this Type type)
    {
        var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);

        return fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly);
    }

    public static IEnumerable<T> GetConstantsValues<T>(this Type type) where T : class
    {
        var fieldInfos = GetConstants(type);

        return fieldInfos.Select(fi => fi.GetRawConstantValue() as T);
    }
}

1
เห็นได้ชัดว่านี่คือถ้าค่าคงที่ของคุณในประเภทเป็นสตริงทั้งหมด ;-)
bytedev

ทำไมไม่ (ก) ทำให้วิธีการทั่วไป (ข) ทำให้วิธีการกลับมาIEnumerable<T>แทนการIList?
Wai Ha Lee

@WaiHaLee - เรียบร้อยแล้ว :-) แม้ว่าจะเห็นได้ชัดว่ามันยังถือว่าทุกประเภทของ consts ในชั้นเรียนเป็นประเภท T.
bytedev

2

ใช้property.GetConstantValue()เพื่อรับค่า


1
นั่นอาจจะเป็นกรณีที่เมื่อคุณมีคุณสมบัติ - แต่วิธีการที่คุณไม่ได้รับครั้งแรกทรัพย์สินหรือไม่
Wai Ha Lee

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