Làm thế nào tôi có thể nhận được tất cả các hằng số của một loại bằng phản xạ?


Câu trả lời:


263

Mặc dù đó là một mã cũ:

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

Nguồn

Bạn có thể dễ dàng chuyển đổi nó thành mã sạch hơn bằng cách sử dụng generic và 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();
}

Hoặc với một dòng:

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

13
+1 của tôi là trước khi tôi thậm chí vượt qua dòng thứ 2 .. tôi nhận thấy bạn đang thực hiện từng bước với ... mục đích thiết kế của nó ...! đây là SO quan trọng khi người ta cần phải học hỏi từ nó. tôi ước mọi người có kinh nghiệm của bạn sẽ làm như bạn đã làm ở đây.
LoneXcoder

4
Tôi không chắc chắn về các xác nhận liên quan đến IsLiteral và IsInitOnly. Khi kiểm tra có vẻ như đối với các thuộc tính chỉ đọc tĩnh IsLiteral luôn sai - vì vậy IsLiteral là cờ duy nhất bạn cần kiểm tra để tìm các hằng số và bạn có thể bỏ qua IsInitOnly. Tôi đã thử với các loại trường khác nhau (ví dụ String, Int32) để xem liệu điều này có tạo ra sự khác biệt nào không nhưng nó không.
Đánh dấu Watts

49
Ngoài ra, để lấy giá trị của const từ FieldInfo, hãy sử dụng GetRawConstantValue ().
Sam Sippe

@MarkWatts đã đúng. Có thể là hành vi thay đổi kể từ khi điều này được đăng. Trong mọi trường hợp tài liệu IsLiteralnói if its value is written at compile timevà điều đó chỉ đúng với các hằng số, đó là cách nó hoạt động ngay bây giờ (được thử nghiệm từ .NET 4.5.2)
nawfal 23/12/19

52

Nếu bạn muốn lấy các giá trị của tất cả các hằng số của một loại cụ thể, từ loại mục tiêu, đây là một phương thức mở rộng (mở rộng một số câu trả lời trên trang này):

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

Sau đó, cho một lớp học như thế này

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

Bạn có thể có được các stringgiá trị không đổi như thế này:

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

Tại sao không phải thế này : .Where(fi => fi.IsLiteral && !fi.IsInitOnly).Select(x => x.GetRawConstantValue()).OfType<T>().ToList();?
T-moty

17

Kiểu mở rộng:

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
Rõ ràng đây là nếu các hằng số của bạn trên một loại đều là các chuỗi ;-)
bytedev

Tại sao không (a) làm cho các phương thức chung chung, (b) làm cho các phương thức trở lại IEnumerable<T>thay vì một IList?
Ái Hà Lee

@WaiHaLee - Xong :-). Mặc dù rõ ràng nó vẫn giả định tất cả các loại hằng số trong lớp được đề cập là loại T.
bytedev

2

Sử dụng property.GetConstantValue()để có được giá trị.


1
Đó cũng có thể là trường hợp khi bạn tài sản - nhưng làm thế nào để bạn có được tài sản đầu tiên?
Ái Hà Lee

4
Trong .Net 4.5, nó:GetRawConstantValue()
Chris
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.