Wie kann ich alle Konstanten eines Typs durch Reflexion erhalten?

Antworten:

263

Obwohl es ein alter Code ist:

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));
}

Quelle

Sie können es einfach mit Generika und LINQ in saubereren Code konvertieren:

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();
}

Oder mit einer Zeile:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
Gdoron unterstützt Monica
quelle
13
Meine +1 war, bevor ich die 2. Zeile überhaupt überschritten habe. Ich habe bemerkt, dass Sie jeden Schritt mit seinem ... vom Design beabsichtigten Zweck durchlaufen ...! dies ist SO wichtig , wenn man von ihm lernen muss. Ich wünschte, jeder mit Ihrer Erfahrung würde das tun, was Sie hier getan haben.
LoneXcoder
4
Ich bin mir nicht sicher über die Behauptungen in Bezug auf IsLiteral und IsInitOnly. Beim Testen scheint IsLiteral für statische schreibgeschützte Eigenschaften immer falsch zu sein. Daher ist IsLiteral das einzige Flag, das Sie überprüfen müssen, um Konstanten zu finden, und Sie können IsInitOnly ignorieren. Ich habe mit verschiedenen Feldtypen (z. B. String, Int32) versucht, festzustellen, ob dies einen Unterschied macht, aber dies war nicht der Fall.
Mark Watts
49
Verwenden Sie außerdem GetRawConstantValue (), um den Wert der Konstante aus FieldInfo abzurufen.
Sam Sippe
@ MarkWatts ist richtig. Möglicherweise hat sich das Verhalten geändert, seit dies veröffentlicht wurde. In jedem Fall ist die Dokumentation von IsLiteralsagt if its value is written at compile timeund das gilt nur für Konstanten, so wie es sich jetzt verhält (getestet ab .NET 4.5.2)
nawfal
52

Wenn Sie die Werte aller Konstanten eines bestimmten Typs vom Zieltyp abrufen möchten , finden Sie hier eine Erweiterungsmethode (Erweiterung einiger Antworten auf dieser Seite):

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();
    }
}

Dann für eine Klasse wie diese

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

Sie können die stringkonstanten Werte wie folgt erhalten:

List<string> result = typeof(MyFruitKeys).GetAllPublicConstantValues<string>();
//result[0] == "apple"
//result[1] == "plum"
//result[2] == "peach"
BCA
quelle
Warum nicht das : .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();?
T-Moty
17

Als Typerweiterungen:

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);
    }
}
bytedev
quelle
1
Offensichtlich ist dies, wenn Ihre Konstanten auf einem Typ alle Zeichenfolgen sind ;-)
bytedev
Warum nicht (a) die Methoden generisch machen, (b) die Methoden IEnumerable<T>anstelle von a zurückgeben IList?
Wai Ha Lee
@WaiHaLee - Fertig :-). Obwohl offensichtlich immer noch davon ausgegangen wird, dass alle Arten von Konstanten in der betreffenden Klasse vom Typ T sind
bytedev
2

Verwenden Sie property.GetConstantValue(), um Wert zu erhalten.

Reza Bayat
quelle
1
Dass auch der Fall sein kann , wenn Sie haben die Eigenschaft - aber wie sehen Sie zuerst die Eigenschaft bekommen?
Wai Ha Lee
4
In .Net 4.5 ist es:GetRawConstantValue()
Chris