नेस्टेड ऑब्जेक्ट के गुण प्राप्त करने के लिए C # में प्रतिबिंब का उपयोग करना


82

निम्नलिखित वस्तुओं को देखते हुए:

public class Customer {
    public String Name { get; set; }
    public String Address { get; set; }
}

public class Invoice {
    public String ID { get; set; }
    public DateTime Date { get; set; }
    public Customer BillTo { get; set; }
}

मैं एक Invoiceकी Nameसंपत्ति प्राप्त करने के माध्यम से जाने के लिए प्रतिबिंब का उपयोग करना चाहता हूं Customer। यहाँ मैं इसके बाद क्या कर रहा हूँ, यह मानते हुए कि यह कोड काम करेगा:

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);

बेशक, यह विफल रहता है क्योंकि "बिल्टो.एड्रेस" Invoiceक्लास की वैध संपत्ति नहीं है ।

इसलिए, मैंने इस अवधि में स्ट्रिंग को टुकड़ों में विभाजित करने के लिए एक विधि लिखने की कोशिश की, और उन वस्तुओं को चलाएं जो अंतिम मूल्य की तलाश कर रहे थे जिसमें मैं रुचि रखता था। यह ठीक काम करता है, लेकिन मैं इसके साथ पूरी तरह से सहज नहीं हूं:

public Object GetPropValue(String name, Object obj) {
    foreach (String part in name.Split('.')) {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        PropertyInfo info = type.GetProperty(part);
        if (info == null) { return null; }

        obj = info.GetValue(obj, null);
    }
    return obj;
}

इस पद्धति को कैसे बेहतर बनाया जाए, या इस समस्या को हल करने का एक बेहतर तरीका?

EDIT पोस्ट करने के बाद, मैंने कुछ संबंधित पोस्ट देखीं ... ऐसा कोई उत्तर प्रतीत नहीं होता है जो विशेष रूप से इस प्रश्न को संबोधित करता है। इसके अलावा, मैं अभी भी अपने कार्यान्वयन पर प्रतिक्रिया चाहूंगा।


बस जिज्ञासु, यदि आपका GetDesiredInvoiceरिटर्न आपको सीधे वस्तु का प्रकार देता है तो सीधे Invoiceउपयोग क्यों नहीं inv.BillTo.Name?
राम

मैं वास्तव में इसे थोड़ा अलग तरीके से उपयोग कर रहा हूं, बस मेरे उदाहरण के लिए सरल किया गया है। मैं एक ऑब्जेक्ट ले रहा हूं और इसे एक प्रोसेसर में पास कर रहा हूं जो इसे मुद्रण के लिए टेम्पलेट के साथ विलय करता है।
झेजिंग्स

यह सिर्फ थोड़ा "जानवर-बल" लगा और ऐसा लगा कि बेहतर तरीका होना चाहिए। अब तक के जवाबों से, हालांकि, ऐसा लगता है कि मैं पूरी तरह से आधार नहीं था।
झेजिंग्स

3
मैंने सोचा था कि मैं ऐसा करने के लिए पागल था, लेकिन, ऐसा लगता है जैसे किसी को वैसी ही समस्या हुई हो जैसी मैंने की थी। जिस तरह से महान समाधान
Marcello Grechi

एक्सटेंशन विधियों के रूप में इनका उपयोग करने के लिए किसी अन्य विषय पर मेरे उत्तर को भी देखें: stackoverflow.com/questions/1196991/…
jheddings

जवाबों:


12

मुझे वास्तव में लगता है कि आपका तर्क ठीक है। व्यक्तिगत रूप से, मैं संभवतः इसे चारों ओर बदल दूंगा ताकि आप ऑब्जेक्ट को पहले पैरामीटर के रूप में पास करें (जो कि प्रॉपर्टीइन्फो.गेटवैल्यू के साथ अधिक इनलाइन है, इसलिए कम आश्चर्य की बात है)।

मैं भी शायद इसे GetNestedPropertyValue की तरह कुछ और कहूंगा, यह स्पष्ट करने के लिए कि यह संपत्ति स्टैक को खोजता है।


मापदंडों को पुनः व्यवस्थित करने और सुझाए गए नाम परिवर्तन पर अच्छा कॉल।
itowlson

प्रतिक्रिया के लिए धन्यवाद ... मैंने अपने कार्यान्वयन में दोनों सुझाव दिए हैं। मैंने इसे Objectकक्षा में एक विस्तार विधि में बदल दिया , जो मापदंडों को फिर से व्यवस्थित करने पर बिंदु को मजबूत करता है।
झेजिंग्स

क्यों स्वीकार किया गया जवाब? यह मुझे एक नेस्टेड ऑब्जेक्ट की संपत्ति प्राप्त करने में मदद नहीं करता है जैसा कि ओपी ने पूछा था।
लेविटिकन

@Levitikon ओपी का दूसरा कोड कोड एक अच्छा तरीका है जो करने के लिए कहा गया था। यह मेरी पूरी बात थी। कोड में कुछ भी गलत नहीं है क्योंकि उत्तर में ही पोस्ट किया गया है।
रीड कोपसे

27

मैं निम्न विधि (नेस्टेड क्लासेस) जैसे गुणों को प्राप्त करने के लिए निम्न विधि का उपयोग करता हूं

"संपत्ति"

"गली का पता"

"पता.काउंट्री.नाम"

    public static object GetPropertyValue(object src, string propName)
    {
        if (src == null) throw new ArgumentException("Value cannot be null.", "src");
        if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");

        if(propName.Contains("."))//complex type nested
        {
            var temp = propName.Split(new char[] { '.' }, 2);
            return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
        }
        else
        {
            var prop = src.GetType().GetProperty(propName);
            return prop != null ? prop.GetValue(src, null) : null;
        }
    }

यहाँ Fiddle: https://dotnetfiddle.net/PvKRH0 है


यदि संपत्ति शून्य है, तो यह काम नहीं करेगा, जाँचने की आवश्यकता है src शुरुआत में काम करने से पहले शून्य है।
पूर्ति

@Furtiro हाँ यकीन है, काम नहीं कर सकता है कि src (या propName) शून्य है। मैंने फेंक अपवाद जोड़ा। धन्यवाद
Devt

मदद करने में खुशी ! लेकिन यह मेरे लिए ट्रिपल नेस्टिंग संपत्ति के साथ काम नहीं करता है, यह 2, उदास लेकिन महान कोड के बाद बंद हो जाता है!
फर्तिरो

@Furtiro अजीब है, काम करना है जैसा कि आप मुझे अभी पोस्ट में जोड़े गए फिडल पर देख सकते हैं। एक नज़र है, शायद आप अपने मुद्दे को पा सकते हैं।
15

जब हम नेस्टेड क्लास का उपयोग करते हैं, तो क्या गेटप्रॉपर्टी काम करने के लिए आपके पास एक ही तरीका होगा? यानी var गुण = type.GetProperty (सॉर्टप्रॉपर्टी); नेस्टेड क्लास के साथ विफल रहता है (जैसा कि हमें एक अशक्त परिणाम मिलता है), मुझे लगता है कि आपका समाधान इसका जवाब दे सकता है। (पूर्ण विवरण के लिए, यहां दिया गया समाधान stackoverflow.com/questions/11336713/… नेस्टेड क्लास के साथ विफल रहता है)
Kynao

13

मुझे पता है कि मुझे पार्टी में थोड़ी देर हो गई है, और जैसा कि दूसरों ने कहा, आपका कार्यान्वयन ठीक है
... सरल उपयोग के मामलों के लिए
हालाँकि, मैंने एक पुस्तकालय विकसित किया है जो कि केस, Pather.CSharp का उपयोग करने के बिलकुल हल करता है ।
यह नुगेट पैकेज के रूप में भी उपलब्ध है ।

इसका मुख्य वर्ग Resolverइसकी Resolveविधि के साथ है ।
आप इसे एक ऑब्जेक्ट और संपत्ति पथ पास करते हैं , और यह वांछित मान लौटाएगा ।

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");

लेकिन यह अधिक जटिल संपत्ति पथों को भी हल कर सकता है , जिसमें सरणी और शब्दकोश पहुंच शामिल है।
तो, उदाहरण के लिए, अपने अगर Customerथा एकाधिक पतों

public class Customer {
    public String Name { get; set; }
    public IEnumerable<String> Addresses { get; set; }
}

आप दूसरे का उपयोग कर सकते हैं Addresses[1]

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");

2
यह कैसे निहित गुणों के लिए अशक्त वस्तुओं को संभालता है अर्थात NullObject.Id के साथ NullObject उदाहरण के लिए इनवॉइस पर अशक्त होने के नाते?
AVFC_Bubble88

10

आपको ACTUAL ऑब्जेक्ट का उपयोग करना होगा, जिस पर आपको प्रतिबिंब का उपयोग करने की आवश्यकता है। यही है जो मेरा मतलब है:

इसके अलावा:

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);

ऐसा करें (टिप्पणी के आधार पर संपादित):

Invoice inv = GetDesiredInvoice();  // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);

PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);

अधिक जानकारी के लिए इस पोस्ट को देखें: किसी वस्तु की संपत्ति का एक गुण सेट करने के लिए प्रतिबिंब का उपयोग करना


उत्तर के लिए धन्यवाद ... मुझे पता है कि पहले स्तर की संपत्ति का मूल्य कैसे प्राप्त किया जाए, लेकिन मैं सोच रहा था कि कैसे एक नेस्टेड प्राप्त किया जाए। मेरे वास्तविक एप्लिकेशन में, मेरी वास्तविक वस्तु तक पहुंच नहीं है।
झेजिंग्स

1
सीधे नेस्टेड गुण प्राप्त करने की आवश्यकता है। मैं ऐसी स्थिति में भी हूं जहां "इनवॉइस" टी है और मेरे पास "प्रॉपर्टीप्रोफर्टी.प्रॉपर्टी" पथ का एक स्ट्रिंग है। प्रत्येक संपत्ति के साथ चारों ओर चक्कर नहीं लगाया जा सकता।
लेविटिकन

इसने मेरे लिए यह किया। मुझे BillTo.Address के साथ कोई भाग्य नहीं था। मैं उत्सुक हूं कि क्या संपत्ति सेट करना उसी तरह से काम करता है?
युसूफ_यूरजादे

7

पार्टी में बहुत देर तक आवाज़ नहीं करने की उम्मीद में, मैं अपना समाधान जोड़ना चाहूंगा: निश्चित रूप से इस स्थिति में पुनरावृत्ति का उपयोग करें

public static Object GetPropValue(String name, object obj, Type type)
    {
        var parts = name.Split('.').ToList();
        var currentPart = parts[0];
        PropertyInfo info = type.GetProperty(currentPart);
        if (info == null) { return null; }
        if (name.IndexOf(".") > -1)
        {
            parts.Remove(currentPart);
            return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
        } else
        {
            return info.GetValue(obj, null).ToString();
        }
    }

6

आप अपने "असुविधा" के स्रोत की व्याख्या नहीं करते हैं, लेकिन आपका कोड मूल रूप से मुझे ध्वनि लगता है।

केवल एक चीज जो मैं सवाल करूंगा वह है त्रुटि से निपटने की। यदि कोड एक अशक्त संदर्भ के माध्यम से या संपत्ति के नाम मौजूद नहीं है, तो आप अशक्त वापस आ जाते हैं। यह त्रुटियों को छुपाता है: यह जानना मुश्किल है कि क्या यह शून्य वापस आया क्योंकि कोई बिल्टो ग्राहक नहीं है, या क्योंकि आपने इसे "बिल्टो.एड्रेस" के नाम से याद किया है ... या क्योंकि बिल्टो ग्राहक है, और इसका पता शून्य है! मैं इन मामलों में विधि को दुर्घटनाग्रस्त होने और जलने देता हूं - बस अपवाद को भागने दें (या हो सकता है कि इसे मित्रवत तरीके से लपेटें)।


3

यहां एक और कार्यान्वयन है जो एक नेस्टेड संपत्ति को छोड़ देगा यदि यह एक एन्यूमरेटर है और गहरा जारी है। टाइप स्ट्रिंग के गुण एन्यूमरेशन चेक से प्रभावित नहीं होते हैं।

public static class ReflectionMethods
{
    public static bool IsNonStringEnumerable(this PropertyInfo pi)
    {
        return pi != null && pi.PropertyType.IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this object instance)
    {
        return instance != null && instance.GetType().IsNonStringEnumerable();
    }

    public static bool IsNonStringEnumerable(this Type type)
    {
        if (type == null || type == typeof(string))
            return false;
        return typeof(IEnumerable).IsAssignableFrom(type);
    }

    public static Object GetPropValue(String name, Object obj)
    {
        foreach (String part in name.Split('.'))
        {
            if (obj == null) { return null; }
            if (obj.IsNonStringEnumerable())
            {
                var toEnumerable = (IEnumerable)obj;
                var iterator = toEnumerable.GetEnumerator();
                if (!iterator.MoveNext())
                {
                    return null;
                }
                obj = iterator.Current;
            }
            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }

            obj = info.GetValue(obj, null);
        }
        return obj;
    }
}

इस सवाल पर और पर आधारित है

कैसे पता करें कि प्रॉपर्टीइंफो बेरील द्वारा संग्रह है

मैं इसका उपयोग MVC परियोजना में गतिशील रूप से उदाहरण के आधार पर संपत्ति को पास करने के लिए अपने डेटा को गतिशील रूप से करने के लिए करता हूं :

result = result.OrderBy((s) =>
                {
                    return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
                }).ToList();

जहाँ BookingItems वस्तुओं की एक सूची है।


2
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
            {
                if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
                    throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
                if (PropertName.Split('.').Length == 1)
                    return t.GetType().GetProperty(PropertName);
                else
                    return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
            }

1
   if (info == null) { /* throw exception instead*/ } 

मैं वास्तव में एक अपवाद फेंक दूंगा यदि वे ऐसी संपत्ति का अनुरोध करते हैं जो मौजूद नहीं है। जिस तरह से आपने इसे कोडित किया है, अगर मैं गेटप्रॉपव्यू कहता हूं और यह अशक्त है, तो मुझे नहीं पता कि इसका मतलब है कि संपत्ति मौजूद नहीं थी, या संपत्ति मौजूद नहीं थी, लेकिन यह मूल्य शून्य था।


इसके अलावा, लूप के बाहर अशक्त होने के लिए चेक को स्थानांतरित करें।
केविन ब्रॉक

क्षमा करें, obj के बार-बार उपयोग को नहीं देखा। अपने मापदंडों को बदलने के लिए यह अच्छा प्रोग्रामिंग अभ्यास नहीं है, इससे भविष्य में भ्रम हो सकता है। लूप के भीतर पार करने के लिए obj पैरामीटर के लिए एक अलग चर का उपयोग करें।
केविन ब्रॉक

केविन: एक अलग चर का उपयोग करने के लिए, उसे या तो इसे अंत में obj करने के लिए असाइन करना होगा, या विधि को पुनरावर्ती बनाना होगा। व्यक्तिगत रूप से, मुझे नहीं लगता कि यह एक समस्या है (हालांकि एक अच्छी टिप्पणी अच्छी होगी ...)
रीड कोपसी

1
@Levitikon ओपी ने कहा "इस पद्धति को कैसे बेहतर बनाया जाए, या इस समस्या को हल करने का एक बेहतर तरीका?" इस प्रकार यह एक उत्तर है और एक टिप्पणी नहीं है, क्योंकि ओपी ने सुधार के लिए कहा, जो कि यह एक सुधार है।
एरोनल्स

1
    public static string GetObjectPropertyValue(object obj, string propertyName)
    {
        bool propertyHasDot = propertyName.IndexOf(".") > -1;
        string firstPartBeforeDot;
        string nextParts = "";

        if (!propertyHasDot)
            firstPartBeforeDot = propertyName.ToLower();
        else
        {
            firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
            nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
        }

        foreach (var property in obj.GetType().GetProperties())
            if (property.Name.ToLower() == firstPartBeforeDot)
                if (!propertyHasDot)
                    if (property.GetValue(obj, null) != null)
                        return property.GetValue(obj, null).ToString();
                    else
                        return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
                else
                    return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
        throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
    }

1
यह वर्णन करें कि आपकी शिथिलता क्या है, यह ओपी को कैसे मदद करेगा?
DontVoteMeDown जुआन

1

मैं अपना समाधान साझा करना चाहता था, हालांकि इसमें बहुत देर हो सकती है। यह समाधान मुख्य रूप से जांचने के लिए है कि क्या नेस्टेड संपत्ति मौजूद है। लेकिन जरूरत पड़ने पर प्रॉपर्टी की वैल्यू लौटाने के लिए इसे आसानी से टाल दिया जा सकता है।

private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
        {
            //***
            //*** Check if the property name is a complex nested type
            //***
            if (propertyName.Contains("."))
            {
                //***
                //*** Get the first property name of the complex type
                //***
                var tempPropertyName = propertyName.Split(".", 2);
                //***
                //*** Check if the property exists in the type
                //***
                var prop = _GetPropertyInfo(type, tempPropertyName[0]);
                if (prop != null)
                {
                    //***
                    //*** Drill down to check if the nested property exists in the complex type
                    //***
                    return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
            }
        }

मुझे इस समाधान के साथ आने के लिए कुछ पोस्टों का उल्लेख करना था। मुझे लगता है कि यह कई नेस्टेड संपत्ति प्रकारों के लिए काम करेगा।


0

जब मुझे उसी समस्या को हल करने की आवश्यकता थी, तो मेरा इंटरनेट कनेक्शन डाउन हो गया था, इसलिए मुझे 'पहिया का फिर से आविष्कार' करना पड़ा:

static object GetPropertyValue(Object fromObject, string propertyName)
{
    Type objectType = fromObject.GetType();
    PropertyInfo propInfo = objectType.GetProperty(propertyName);
    if (propInfo == null && propertyName.Contains('.'))
    {
        string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
        propInfo = objectType.GetProperty(firstProp);
        if (propInfo == null)//property name is invalid
        {
            throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
        }
        return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
    }
    else
    {
        return propInfo.GetValue(fromObject, null);
    }
}

बहुत यकीन है कि यह किसी भी स्ट्रिंग के लिए समस्या को हल करता है जो संपत्ति के नाम के लिए उपयोग करते हैं, चाहे घोंसले के शिकार की परवाह किए बिना, जब तक कि सब कुछ एक संपत्ति है।


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