Câu hỏi GetProperties () để trả về tất cả các thuộc tính cho một hệ thống phân cấp thừa kế giao diện


Giả sử hệ thống phân cấp thừa kế giả định sau:

public interface IA
{
  int ID { get; set; }
}

public interface IB : IA
{
  string Name { get; set; }
}

Sử dụng phản ánh và thực hiện cuộc gọi sau:

typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance) 

sẽ chỉ mang lại các thuộc tính của giao diện IB, đó là "Name".

Nếu chúng tôi thực hiện một thử nghiệm tương tự trên mã sau đây,

public abstract class A
{
  public int ID { get; set; }
}

public class B : A
{
  public string Name { get; set; }
}

cuộc gọi typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) sẽ trả về một mảng PropertyInfo đối tượng cho "ID"và"Name".

Có cách nào dễ dàng để tìm tất cả các thuộc tính trong hệ thống phân cấp thừa kế cho các giao diện như trong ví dụ đầu tiên?


76
2017-12-11 09:51


gốc




Các câu trả lời:


Tôi đã tinh chỉnh mã ví dụ của @Marc Gravel thành một phương thức mở rộng hữu ích đóng gói cả hai lớp và các giao diện. Nó cũng thêm các thuộc tính giao diện đầu tiên mà tôi tin là hành vi mong đợi.

public static PropertyInfo[] GetPublicProperties(this Type type)
{
    if (type.IsInterface)
    {
        var propertyInfos = new List<PropertyInfo>();

        var considered = new List<Type>();
        var queue = new Queue<Type>();
        considered.Add(type);
        queue.Enqueue(type);
        while (queue.Count > 0)
        {
            var subType = queue.Dequeue();
            foreach (var subInterface in subType.GetInterfaces())
            {
                if (considered.Contains(subInterface)) continue;

                considered.Add(subInterface);
                queue.Enqueue(subInterface);
            }

            var typeProperties = subType.GetProperties(
                BindingFlags.FlattenHierarchy 
                | BindingFlags.Public 
                | BindingFlags.Instance);

            var newPropertyInfos = typeProperties
                .Where(x => !propertyInfos.Contains(x));

            propertyInfos.InsertRange(0, newPropertyInfos);
        }

        return propertyInfos.ToArray();
    }

    return type.GetProperties(BindingFlags.FlattenHierarchy
        | BindingFlags.Public | BindingFlags.Instance);
}

102
2018-03-14 22:36



Pure Brilliance! Cảm ơn bạn điều này giải quyết được một vấn đề tôi đã có tương tự như câu hỏi của op. - kamui
Không có đủ upvotes trên thế giới cho việc này. - Chao
Tài liệu tham khảo của bạn để BindingFlags.FlattenHierarchy là dư thừa nhìn thấy như bạn cũng đang sử dụng BindingFlags.Instance. - Chris Ward
Không cần đệ quy hoặc xếp hàng vì GetInterfaces () đã trả về tất cả các giao diện được thực hiện bởi một kiểu. Như Marc đã lưu ý, không có hệ thống phân cấp, vậy tại sao chúng ta phải "recurse" trên bất cứ thứ gì? - glopes
@ FrankyHollywood đó là lý do tại sao bạn không sử dụng GetProperties. Bạn dùng GetInterfaces trên loại bắt đầu sẽ trả về danh sách phẳng của tất cả các giao diện và chỉ cần thực hiện GetProperties trên mỗi giao diện. Không cần đệ quy. Không có kiểu thừa kế hoặc cơ sở nào trong các giao diện. - glopes


Type.GetInterfaces trả về phân cấp phẳng, vì vậy không cần phải có một đệ quy đệ quy.

Toàn bộ phương thức có thể được viết chính xác hơn bằng LINQ:

public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
{
    if (!type.IsInterface)
        return type.GetProperties();

    return (new Type[] { type })
           .Concat(type.GetInterfaces())
           .SelectMany(i => i.GetProperties());
}

49
2017-11-05 20:13



Điều này chắc chắn sẽ là câu trả lời đúng! Không cần phải đệ quy clunky. - glopes
Câu trả lời chắc chắn cảm ơn bạn. Làm thế nào chúng ta có thể nhận được giá trị của một tài sản trong giao diện cơ sở? - ilker unal
@ilkerunal: Cách thông thường: Gọi GetValue trên các lấy PropertyInfo, chuyển trường hợp của bạn (có giá trị thuộc tính để lấy) làm tham số. Thí dụ: var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i => i.Name == "Count").GetValue(list); ← sẽ trả về 3, mặc dù Countđược xác định trong ICollection, không phải IList. - Douglas
Giải pháp này có sai sót ở chỗ nó có thể trả về các thuộc tính cùng tên nhiều lần. Cần dọn dẹp thêm các kết quả cho một danh sách đặc tính riêng biệt. Câu trả lời được chấp nhận là giải pháp đúng hơn vì nó đảm bảo các thuộc tính trả về với các tên duy nhất và làm như vậy bằng cách lấy một thuộc tính gần nhất trong chuỗi thừa kế. - user3524983
@ user3524983: Bạn có thể đưa ra ví dụ không? Tôi chỉ thử nó và chỉ có một tài sản cho một tên nhất định. - Douglas


Phân cấp giao diện là một nỗi đau - họ không thực sự "kế thừa" như vậy, vì bạn có thể có nhiều "cha mẹ" (vì muốn có một thuật ngữ tốt hơn).

"Flattening" (một lần nữa, không hoàn toàn đúng hạn) hệ thống phân cấp có thể liên quan đến việc kiểm tra tất cả các giao diện mà giao diện thực hiện và làm việc từ đó ...

interface ILow { void Low();}
interface IFoo : ILow { void Foo();}
interface IBar { void Bar();}
interface ITest : IFoo, IBar { void Test();}

static class Program
{
    static void Main()
    {
        List<Type> considered = new List<Type>();
        Queue<Type> queue = new Queue<Type>();
        considered.Add(typeof(ITest));
        queue.Enqueue(typeof(ITest));
        while (queue.Count > 0)
        {
            Type type = queue.Dequeue();
            Console.WriteLine("Considering " + type.Name);
            foreach (Type tmp in type.GetInterfaces())
            {
                if (!considered.Contains(tmp))
                {
                    considered.Add(tmp);
                    queue.Enqueue(tmp);
                }
            }
            foreach (var member in type.GetMembers())
            {
                Console.WriteLine(member.Name);
            }
        }
    }
}

15
2017-12-11 10:02



Tôi không đồng ý. Với tất cả sự kính trọng đối với Marc, câu trả lời này cũng không nhận ra rằng GetInterfaces () đã trả về tất cả các giao diện được triển khai cho một kiểu. Chính xác vì không có "hệ thống phân cấp", không cần đệ quy hoặc xếp hàng. - glopes


Chính xác cùng một vấn đề có giải pháp được mô tả đây.

FlattenHierarchy không hoạt động btw. (chỉ trên các vars tĩnh. nói như vậy trong intellisense)

Cách giải quyết. Cẩn thận với các bản sao.

PropertyInfo[] pis = typeof(IB).GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type[] tt = typeof(IB).GetInterfaces();
PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance);

3
2017-12-11 10:06





điều này làm việc độc đáo và tẻ nhạt đối với tôi trong một chất kết dính mô hình MVC tùy chỉnh. Nên có thể ngoại suy cho bất kỳ kịch bản phản ánh mặc dù. Vẫn là loại stinks mà nó quá vượt qua

    var props =  bindingContext.ModelType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).ToList();

    bindingContext.ModelType.GetInterfaces()
                      .ToList()
                      .ForEach(i => props.AddRange(i.GetProperties()));

    foreach (var property in props)

1
2017-12-14 14:38





Trả lời @douglas và @ user3524983, câu trả lời sau đây phải trả lời câu hỏi của OP:

    static public IEnumerable<PropertyInfo> GetPropertiesAndInterfaceProperties(this Type type, BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.Instance)
    {
        if (!type.IsInterface) {
            return type.GetProperties( bindingAttr);
        }

        return type.GetInterfaces().Union(new Type[] { type }).SelectMany(i => i.GetProperties(bindingAttr)).Distinct();
    }

hoặc, đối với một tài sản cá nhân:

    static public PropertyInfo GetPropertyOrInterfaceProperty(this Type type, string propertyName, BindingFlags bindingAttr = BindingFlags.Public|BindingFlags.Instance)
    {
        if (!type.IsInterface) {
            return type.GetProperty(propertyName, bindingAttr);
        }

        return type.GetInterfaces().Union(new Type[] { type }).Select(i => i.GetProperty( propertyName, bindingAttr)).Distinct().Where(propertyInfo => propertyInfo != null).Single();
    }

OK lần sau tôi sẽ gỡ lỗi nó trước khi đăng thay vì sau :-)


0
2017-11-14 04:13