Pertanyaan GetProperties () untuk mengembalikan semua properti untuk hierarki warisan antarmuka


Dengan asumsi hierarki warisan hipotetis berikut:

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

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

Menggunakan refleksi dan membuat panggilan berikut:

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

hanya akan menghasilkan properti antarmuka IB, yang mana "Name".

Jika kami melakukan tes serupa pada kode berikut,

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

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

panggilan typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) akan mengembalikan array PropertyInfo benda untuk "ID"dan"Name".

Apakah ada cara mudah untuk menemukan semua properti dalam hierarki warisan untuk antarmuka seperti pada contoh pertama?


76
2017-12-11 09:51


asal


Jawaban:


Saya telah mengubah kode contoh @Marc Gravel menjadi metode ekstensi yang bermanfaat untuk mengenkapsulasi kelas dan antarmuka. Ini juga menambahkan sifat antarmuka pertama yang saya percaya adalah perilaku yang diharapkan.

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



Type.GetInterfaces mengembalikan hierarki yang diratakan, jadi tidak ada kebutuhan untuk keturunan rekursif.

Seluruh metode dapat ditulis jauh lebih ringkas menggunakan 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



Hierarki antarmuka sangat merepotkan - mereka tidak benar-benar "mewarisi" seperti itu, karena Anda dapat memiliki banyak "orang tua" (karena menginginkan istilah yang lebih baik).

"Perataan" (sekali lagi, bukan istilah yang tepat) hirarki mungkin melibatkan memeriksa semua antarmuka yang digunakan antarmuka dan bekerja dari sana ...

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



Persis masalah yang sama memiliki solusi yang dijelaskan sini.

FlattenHierarchy tidak berfungsi btw. (hanya pada vars statis. mengatakan demikian dalam intellisense)

Solusi. Hati-hati dengan duplikat.

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



ini bekerja dengan baik dan singkat untuk saya dalam pengikat model MVC khusus. Seharusnya dapat mengekstrapolasi ke skenario refleksi apa pun. Masih agak bau busuk karena itu juga lewat

    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



Menanggapi @douglas dan @ user3524983, berikut ini harus menjawab pertanyaan 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();
    }

atau, untuk properti individu:

    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 lain kali saya akan men-debug-nya sebelum memposting, bukan setelah :-)


0
2017-11-14 04:13