Bagaimana saya bisa mendapatkan semua konstanta dari tipe dengan refleksi?

Jawaban:

264

Meskipun ini kode lama:

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

Sumber

Anda dapat dengan mudah mengubahnya menjadi kode pembersih menggunakan obat generik dan 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();
}

Atau dengan satu baris:

type.GetFields(BindingFlags.Public | BindingFlags.Static |
               BindingFlags.FlattenHierarchy)
    .Where(fi => fi.IsLiteral && !fi.IsInitOnly).ToList();
gdoron mendukung Monica
sumber
13
+1 saya bahkan sebelum saya melewati baris ke-2 .. saya perhatikan Anda akan melalui setiap langkah dengan ... tujuan yang dimaksudkan oleh desain ...! ini adalah SO penting ketika salah satu kebutuhan untuk belajar dari itu. Saya berharap setiap orang dengan pengalaman Anda akan melakukan seperti yang Anda lakukan di sini.
LoneXcoder
4
Saya tidak yakin tentang pernyataan tentang IsLiteral dan IsInitOnly. Pada pengujian akan terlihat bahwa untuk sifat readonly statis, IsLiteral selalu salah - jadi IsLiteral adalah satu-satunya flag yang perlu Anda periksa untuk menemukan konstanta dan Anda dapat mengabaikan IsInitOnly. Saya mencoba dengan berbagai jenis bidang (mis. String, Int32) untuk melihat apakah ini ada bedanya tetapi tidak.
Mark Watts
49
Juga, untuk mendapatkan nilai const dari FieldInfo, gunakan GetRawConstantValue ().
Sam Sippe
@MarkWatts benar. Mungkin perilaku berubah sejak ini diposting. Dalam setiap kasus, dokumentasi IsLiteralkata if its value is written at compile timedan itu hanya berlaku untuk konstanta, yaitu bagaimana itu berlaku sekarang (diuji pada .NET 4.5.2)
nawfal
52

Jika Anda ingin mendapatkan nilai dari semua konstanta dari tipe tertentu, dari tipe target, berikut adalah metode ekstensi (memperluas beberapa jawaban di halaman ini):

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

Lalu untuk kelas seperti ini

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

Anda dapat memperoleh nilai stringkonstan seperti ini:

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

Sebagai Jenis ekstensi:

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
sumber
1
Jelas ini adalah jika konstanta Anda pada suatu tipe semuanya adalah string ;-)
bytedev
Mengapa tidak (a) membuat metode generik, (b) membuat metode kembali IEnumerable<T>bukan IList?
Wai Ha Lee
@WaiHaLee - Selesai :-). Meskipun jelas itu masih mengasumsikan semua tipe const pada kelas yang dimaksud adalah tipe T.
bytedev
2

Gunakan property.GetConstantValue()untuk mendapatkan nilai.

Reza Bayat
sumber
1
Itu mungkin terjadi ketika Anda memiliki properti - tetapi bagaimana Anda pertama kali mendapatkan properti itu?
Wai Ha Lee
4
Dalam .Net 4.5 itu:GetRawConstantValue()
Chris