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


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

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) की एक सरणी वापस आ जाएगी PropertyInfo वस्तुओं के लिए "ID" तथा "Name"।

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


76
2017-12-11 09:51


मूल




जवाब:


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

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



शुद्ध प्रतिभा! धन्यवाद, इसने एक समस्या हल की जो मैं ओप के सवाल के समान था। - kamui
इसके लिए दुनिया में पर्याप्त उत्थान नहीं हैं। - Chao
BindingFlags.FlattenHierarchy के आपके संदर्भ अनावश्यक हैं क्योंकि आप बाइंडिंगफ्लैग का भी उपयोग कर रहे हैं। इंस्टेंस। - Chris Ward
रिकर्सन या कतारों की कोई आवश्यकता नहीं है क्योंकि GetInterfaces () पहले से ही एक प्रकार द्वारा लागू सभी इंटरफेस लौटाता है। जैसा कि मार्क द्वारा उल्लेख किया गया है, कोई पदानुक्रम नहीं है, तो हमें किसी चीज़ पर "रिकर्स" क्यों करनी चाहिए? - glopes
@ फ्रैंकीहोलीवुड यही कारण है कि आप इसका उपयोग नहीं करते हैं GetProperties। तुम इस्तेमाल GetInterfaces आपके शुरुआती प्रकार पर जो सभी इंटरफेस की फ़्लैटेड सूची लौटाएगा और बस करेगा GetProperties प्रत्येक इंटरफ़ेस पर। रिकर्सन की कोई ज़रूरत नहीं है। इंटरफेस में कोई विरासत या आधार प्रकार नहीं है। - glopes


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

49
2017-11-05 20:13



यह निश्चित रूप से सही जवाब होना चाहिए! गुंजाइश रिकर्सन की कोई ज़रूरत नहीं है। - glopes
ठोस जवाब धन्यवाद। बेस इंटरफेस में हम संपत्ति का मूल्य कैसे प्राप्त कर सकते हैं? - ilker unal
@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। - Douglas
इस समाधान में त्रुटियां हैं कि यह एक ही नाम के गुणों को कई बार वापस कर सकती है। एक अलग संपत्ति सूची के लिए परिणामों की और सफाई की आवश्यकता है। स्वीकार्य उत्तर अधिक सही समाधान है क्योंकि यह अद्वितीय नामों के साथ गुणों को वापस करने की गारंटी देता है और विरासत श्रृंखला में सबसे नज़दीक को पकड़कर ऐसा करता है। - user3524983
@ user3524983: क्या आप कृपया एक उदाहरण दे सकते हैं? मैंने अभी कोशिश की और किसी दिए गए नाम के लिए केवल एक संपत्ति प्राप्त की। - Douglas


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

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

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



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


वास्तव में एक ही समस्या का वर्णन एक वर्कअराउंड है यहाँ

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

3
2017-12-11 10:06





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

    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





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

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


0
2017-11-14 04:13