Soru Bir arabirim devralma hiyerarşisi için tüm özellikleri döndürmek için GetProperties ()


Aşağıdaki hipotetik kalıtım hiyerarşisi varsayarsak:

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

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

Yansıma kullanmak ve aşağıdaki çağrıyı yapmak:

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

sadece arayüzün özelliklerini verecek IB, hangisi "Name".

Aşağıdaki kodda benzer bir test yapsak,

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

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

arama typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance) bir dizi döndürecek PropertyInfo "için nesnelerID" ve "Name".

İlk örnekte olduğu gibi arabirimler için miras hiyerarşisindeki tüm özellikleri bulmanın kolay bir yolu var mı?


76
2017-12-11 09:51


Menşei




Cevaplar:


@Marc Gravel'in örnek kodunu, hem sınıfları hem de arabirimleri kapsülleyen kullanışlı bir uzantı yöntemine dönüştürdüm. Ayrıca, beklenen davranış olduğuna inandığım arabirim özelliklerini de ekliyor.

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



Saf parlaklık! Teşekkürler. Bu, soruna benzer bir sorun yaşadım. - kamui
Bunun için dünyada yeterince upvot yok. - Chao
BindingFlags.FlattenHierarchy referanslarınız BindingFlags.Instance'ı kullandığınız için gereksizdir. - Chris Ward
GetInterfaces () zaten bir tür tarafından uygulanan tüm arabirimleri döndürdüğünden, yineleme veya sıralamaya gerek yoktur. Marc'ın da belirttiği gibi, hiyerarşi yok, o halde niçin bir şey üzerinde "nüks" etmeliyiz? - glopes
@FrankyHollywood bu yüzden kullanmıyorsunuz GetProperties. Kullan GetInterfaces Tüm arayüzlerin düzleştirilmiş listesini döndürecek olan başlangıç ​​tipinizde GetProperties her arayüzde. Yinelemeye gerek yok. Arayüzlerde kalıtım veya taban tipi yoktur. - glopes


Type.GetInterfaces Düzleştirilmiş hiyerarşiyi döndürür, bu nedenle özyinelemeye gerek yoktur.

Tüm yöntem LINQ kullanılarak çok daha kısa bir şekilde yazılabilir:

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



Bu kesinlikle doğru cevap olmalı! Clunky özyinesine gerek yok. - glopes
Katı cevap teşekkür ederim. Bir mülkün değerini temel arayüzde nasıl alabiliriz? - ilker unal
@ilkerunal: Her zamanki yol: Çağrı GetValue Alınan PropertyInfo, örneğinizi (özellik değeri almayı) parametre olarak geçirerek. Örnek: var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i => i.Name == "Count").GetValue(list); ← olsa bile 3, geri dönecek Countiçinde tanımlanmıştır ICollection, değil IList. - Douglas
Bu çözüm, aynı adın özelliklerini birden çok kez döndürmesi nedeniyle kusurlara sahiptir. Farklı bir özellik listesi için sonuçların daha fazla temizlenmesi gerekir. Kabul edilen cevap, benzersiz isimlerle geri dönen mülkleri garanti ettiği ve miras zincirine en yakın olanı yakaladığı için daha doğru çözümdür. - user3524983
@ user3524983: Lütfen bir örnek verebilir misiniz? Sadece denedim ve belirli bir isim için sadece bir özellik var. - Douglas


Arabirim hiyerarşileri bir acıdır - gerçekten "kalıtsal" değildirler, çünkü birden fazla "ebeveyn" e sahip olabilirsiniz (daha iyi bir terim istemek için).

"Düzleştirme" (yine, tam olarak doğru terim değil) hiyerarşi, ara yüzün uyguladığı ve oradan çalıştığı tüm arayüzleri kontrol etmeyi içerebilir.

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



Katılmıyorum. Marc için tüm saygımla, bu cevap aynı zamanda GetInterfaces () 'nin zaten bir tür için uygulanan tüm arabirimleri döndürdüğünü fark etmede başarısız oluyor. Kesin olarak "hiyerarşi" olmadığından, tekrarlama veya sıralamaya gerek yoktur. - glopes


Tam olarak aynı sorun açıklanan bir geçici çözüm var İşte.

FlattenHierarchy btw çalışmıyor. (sadece statik vars. intellisense'de öyle diyor)

Geçici çözüm. Yinelenenlere dikkat edin.

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





Bu, özel bir MVC model bağlayıcısında benim için güzel ve çok çalıştı. Ancak herhangi bir yansıma senaryosunu tahmin edebilecektir. Hala çok geçtiğini düşünüyor.

    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





@Ouglas ve @ user3524983'e yanıt vermek için, OP'nin şu sorusu yanıtlanmalıdır:

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

veya bireysel bir mülk için:

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

Tamam bir dahaki sefere sonra :-) yerine yazmadan önce hata ayıklayacağım


0
2017-11-14 04:13