如何通过反射获取一个类型的所有常量?

211

如何利用反射获取任何类型的所有常量?


5
http://weblogs.asp.net/whaggard/archive/2003/02/20/2708.aspx - David Brabant
5个回答

366

虽然这是旧代码:

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

来源

你可以使用泛型和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();

19
在我通过第二行之前,我就已经点赞了...我注意到你在逐步展示每个步骤的旨在设计目的...!当一个人需要从中学习时,这是如此重要的。我希望每个有你经验的人都能像你在这里所做的那样。 - LoneXcoder
5
关于IsLiteral和IsInitOnly的说法我不确定。经过测试,对于静态readonly属性,IsLiteral似乎总是false - 因此,你只需要检查IsLiteral标志以查找常量,并且可以忽略IsInitOnly。我尝试使用不同的字段类型(例如String、Int32)来查看是否有任何区别,但没有发现差异。 - Mark Watts
60
此外,要获取FieldInfo中const的值,请使用GetRawConstantValue()。 - Sam Sippe
1
@MarkWatts 是正确的。也许自发布以来行为已经改变。无论如何,IsLiteral 的文档说“如果它的值在编译时写入”,这仅适用于常量,这就是它现在的行为方式(截至 .NET 4.5.2)。 - nawfal

99

如果您想从目标类型中获取特定类型的所有常量值,这里是一个扩展方法(对本页上的一些答案进行了扩展):

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"

4
为什么不用这个:.Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList(); - T-moty

25
作为类型扩展:
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
为什么不(a)使方法通用,(b)使方法返回IEnumerable<T>而不是IList - Wai Ha Lee
@WaiHaLee - 已完成 :-). 当然,它仍假定类中所有类型为T的常量类型。 - bytedev

4

使用 property.GetConstantValue() 来获取属性的值。


2
这可能是在你这个属性时的情况 - 但是如何才能首先获得该属性? - Wai Ha Lee
7
在.Net 4.5中,它是:GetRawConstantValue() - Chris

-1
public class Constants
{
    public class InputType
    {
        public const string DOCUMENTPHOTO = "document-photo";
        public const string SELFIEPHOTO = "selfie-photo";
        public const string SELFIEVIDEO = "selfie-video";
        public static List<string> Domain { get { return typeof(Constants.InputType).GetAllPublicConstantValues<string>(); } }
    }
    public class Type
    {
        public const string DRIVINGLICENSE = "driving-license";
        public const string NATIONALID = "national-id";
        public const string PASSPORT = "passport";
        public const string PROOFOFRESIDENCY = "proof-of-residency";
        public static List<string> Domain { get { return typeof(Constants.Type).GetAllPublicConstantValues<string>(); } }
    }
    public class Page
    {
        public const string FRONT = "front";
        public const string BLACK = "back";
        public static List<string> Domain { get { return typeof(Constants.Page).GetAllPublicConstantValues<string>(); } }
    }
    public class FileType
    {
        public const string FRONT = "selfie";
        public const string BLACK = "video";
        public const string DOCUMENT = "document";
        public const string MEDIA = "media";
        public const string CAPTCHA = "captcha";
        public const string DIGITALSIGNATURE = "digitalSignature";
        public static List<string> Domain { get { return typeof(Constants.FileType).GetAllPublicConstantValues<string>(); } }
    }
}

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

使用:var inputTypeDomain = Constants.InputType.Domain;


我没有明白 - 它在做什么? - Justin Greywolf
看起来这只是BCA答案的复制粘贴。 - absoluteAquarian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接