एक इंटरफेस विरासत पदानुक्रम के लिए सभी संपत्तियों को वापस करने के लिए GetProperties ()


96

निम्नलिखित काल्पनिक विरासत पदानुक्रम को मानते हुए:

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

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

प्रतिबिंब का उपयोग करना और निम्नलिखित कॉल करना:

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

केवल इंटरफ़ेस के गुण देगा IB, जो " Name" है।

यदि हमें निम्नलिखित कोड पर एक समान परीक्षण करना था,

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

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

कॉल " " और " " के लिए ऑब्जेक्ट typeof(B).GetProperties(BindingFlags.Public | BindingFlags.Instance)की एक सरणी लौटाएगा ।PropertyInfoIDName

पहले उदाहरण के रूप में इंटरफेस के लिए विरासत पदानुक्रम में सभी गुणों को खोजने का एक आसान तरीका है?

जवाबों:


112

मैंने @Marc बजरी के उदाहरण कोड को एक उपयोगी एक्सटेंशन विधि में बदल दिया है, दोनों वर्गों और इंटरफेस को इनकैप्सुलेट करता है। यह पहले इंटरफ़ेस गुण भी जोड़ता है जो मुझे लगता है कि अपेक्षित व्यवहार है।

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

2
शुद्ध दीप्ति! धन्यवाद, इस समस्या का हल मुझे op के सवाल से मिलता-जुलता था।
kamui

1
BindingFlags.FlattenHierarchy के आपके संदर्भ यह देखकर बेमानी हैं कि आप BindingFlags.Instance का भी उपयोग कर रहे हैं।
क्रिस वार्ड

1
मैंने इसे लागू किया है लेकिन Stack<Type>इसके बजाय एक के साथ Queue<>। एक स्टैक के साथ, वंशावली एक आदेश को बनाए रखता है जैसे कि interface IFoo : IBar, IBazकहां IBar : IBubbleऔर 'इबज: इफब्लबर , the order of reflection becomes: आईबेर , आईबुल , आईबीज , आईफ्लबर , आईफू'।
आइब्रेट

4
GetInterfaces () पहले से ही एक प्रकार से लागू किए गए सभी इंटरफेस लौटाता है, क्योंकि पुनरावृत्ति या कतारों की कोई आवश्यकता नहीं है। जैसा कि मार्क ने कहा, कोई पदानुक्रम नहीं है, इसलिए हमें किसी भी चीज़ पर "पुनर्विचार" क्यों करना चाहिए?
ग्लोप्स

3
@FrankyHollywood यही कारण है कि आप उपयोग नहीं करते हैं GetProperties। आप GetInterfacesअपने शुरुआती प्रकार का उपयोग करते हैं जो सभी इंटरफेस की चपटी सूची को वापस कर देगा और बस GetPropertiesप्रत्येक इंटरफ़ेस पर करेगा। पुनरावृत्ति की कोई आवश्यकता नहीं है। इंटरफेस में कोई विरासत या आधार प्रकार नहीं है।
ग्लोप्स

77

Type.GetInterfaces चपटा पदानुक्रम लौटाता है, इसलिए पुनरावर्ती वंश की आवश्यकता नहीं है।

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

8
यह निश्चित रूप से सही उत्तर होना चाहिए! क्लंकी पुनरावृत्ति के लिए कोई ज़रूरत नहीं है।
ग्लोप्स

ठोस जवाब धन्यवाद। हम आधार इंटरफ़ेस में किसी संपत्ति का मूल्य कैसे प्राप्त कर सकते हैं?
इल्कर अनल

1
@ilkerunal: सामान्य तरीका: पैरामीटर के रूप में आपके उदाहरण (जिसकी संपत्ति का मूल्य प्राप्त करना है) को पास करते हुए, GetValueपुनः प्राप्त PropertyInfoकरें। उदाहरण:, var list = new[] { 'a', 'b', 'c' }; var count = typeof(IList).GetPublicProperties().First(i => i.Name == "Count").GetValue(list);3 वापस आ जाएगा, भले ही Countभीतर परिभाषित किया गया हो ICollection, नहीं IList
डगलस

2
इस समाधान में खामियां हैं कि यह एक ही नाम के गुण कई बार लौटा सकता है। एक अलग संपत्ति सूची के लिए परिणामों की आगे की सफाई आवश्यक है। स्वीकृत उत्तर अधिक सही समाधान है क्योंकि यह अद्वितीय नामों के साथ रिटर्निंग गुणों की गारंटी देता है और ऐसा विरासत श्रृंखला में निकटतम को पकड़कर करता है।
user3524983

1
@AntWaters GetInterfaces की आवश्यकता नहीं है यदि typeएक वर्ग है, क्योंकि ठोस वर्ग उन सभी गुणों को लागू करता है, जो विरासत श्रृंखला के नीचे सभी इंटरफेस में परिभाषित हैं। GetInterfacesउस परिदृश्य में उपयोग करने से सभी संपत्तियों का दोहराव हो जाएगा।
क्रिस शॉलर

15

इंटरफ़ेस पदानुक्रम एक दर्द है - वे वास्तव में "विरासत" नहीं करते हैं, जैसे कि आपके पास कई "माता-पिता" हो सकते हैं (बेहतर अवधि के लिए)।

"चपटे" (फिर से, बिल्कुल सही शब्द नहीं) पदानुक्रम में इंटरफ़ेस को लागू करने और वहां से काम करने वाले सभी इंटरफेस की जांच शामिल हो सकती है ...

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

7
मैं असहमत हूं। मार्क के लिए सभी उचित सम्मान के साथ, यह उत्तर भी महसूस करने में विफल रहता है कि गेटइंटरफेस () पहले से ही एक प्रकार के लिए लागू किए गए सभी इंटरफेस को वापस कर देता है। ठीक है क्योंकि "पदानुक्रम" नहीं है, पुनरावृत्ति या कतारों की कोई आवश्यकता नहीं है।
16

3

वास्तव में एक ही समस्या यहाँ वर्णित एक समाधान है

FlattenHierarchy काम नहीं करता है btw। (केवल स्टैटिक वर्जन पर। इंटैलिजेंस में ऐसा कहते हैं)

वैकल्पिक हल। नकलचियों से सावधान रहें।

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

2

@Douglas और @ user3524983 पर प्रतिक्रिया देते हुए, निम्नलिखित को ओपी के प्रश्न का उत्तर देना चाहिए:

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

या, एक व्यक्तिगत संपत्ति के लिए:

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

ठीक है अगली बार मैं इसे पोस्ट करने से पहले डिबग करूंगा उसके बाद :-)


1

यह एक कस्टम एमवीसी मॉडल बाइंडर में मेरे लिए अच्छी तरह से और कठिन काम करता है। हालांकि किसी भी प्रतिबिंब परिदृश्य को एक्सट्रपलेशन करने में सक्षम होना चाहिए। अभी भी इस तरह की बदबू आ रही है

    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)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.