अनाम प्रकार की संपत्ति को C # में कैसे एक्सेस करें?


125

मेरे पास यह है:

List<object> nodes = new List<object>(); 

nodes.Add(
new {
    Checked     = false,
    depth       = 1,
    id          = "div_" + d.Id
});

... और मैं सोच रहा हूं कि क्या मैं अनाम वस्तु की "चेक की गई" संपत्ति हड़प सकता हूं। मुझे यकीन नहीं है कि यह भी संभव है। ऐसा करने की कोशिश की:

if (nodes.Any(n => n["Checked"] == false)) ... लेकिन यह काम नहीं करता है।

धन्यवाद

जवाबों:


63

यदि आप गुमनाम प्रकारों की दृढ़ता से टाइप की गई सूची चाहते हैं, तो आपको सूची को एक अनाम प्रकार भी बनाना होगा। ऐसा करने का सबसे आसान तरीका एक अनुक्रम जैसे कि सूची में एक सरणी, जैसे

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

तब आप इसे इस तरह एक्सेस कर पाएंगे:

nodes.Any(n => n.Checked);

कंपाइलर के काम करने के तरीके के कारण, सूची को बनाने के बाद निम्नलिखित को भी काम करना चाहिए क्योंकि अनाम प्रकारों की संरचना समान होती है इसलिए वे भी उसी प्रकार के होते हैं। हालांकि यह सत्यापित करने के लिए मेरे पास संकलक नहीं है।

nodes.Add(new { Checked = false, /* etc */ });

263

यदि आप ऑब्जेक्ट को प्रकार के रूप में संग्रहीत कर रहे हैं object, तो आपको प्रतिबिंब का उपयोग करने की आवश्यकता है। यह किसी भी ऑब्जेक्ट प्रकार, अनाम या अन्यथा का सच है। ऑब्जेक्ट ओ पर, आप इसका प्रकार प्राप्त कर सकते हैं:

Type t = o.GetType();

फिर उसमें से आप एक संपत्ति देखते हैं:

PropertyInfo p = t.GetProperty("Foo");

फिर उस से आप एक मूल्य प्राप्त कर सकते हैं:

object v = p.GetValue(o, null);

C # 4 के अपडेट के लिए यह उत्तर लंबे समय से अधिक है:

dynamic d = o;
object v = d.Foo;

और अब C # 6 में एक और विकल्प:

object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);

ध्यान दें कि का उपयोग करके ?.हम कारण जिसके परिणामस्वरूप vहोने के लिए nullतीन अलग-अलग स्थितियों में!

  1. o है null , इसलिए कोई वस्तु नहीं है
  2. oगैर है- nullलेकिन उसके पास कोई संपत्ति नहीं हैFoo
  3. oएक संपत्ति है, Fooलेकिन इसका वास्तविक मूल्य होता है null

तो यह पहले के उदाहरणों के बराबर नहीं है, लेकिन समझ में आ सकता है कि क्या आप तीनों मामलों को एक जैसा मानना ​​चाहते हैं।


4
अब तक पहले कभी डायनेमिक का उपयोग नहीं किया गया था। .NET 4.0 के लिए अच्छा अपडेट
एलन

यदि संपत्ति मौजूद नहीं है ( object v = d.Foo), तो c # 4 समाधान में आपको रनटाइम अपवाद मिलेगा , जबकि GetValue(o, null)मौजूद नहीं होने पर अशक्त होगा।
याकोवहातम

1
नहीं, GetPropertyवापस आ जाएगा null, और GetValueअगर वह पास हो जाएगा null, तो समग्र प्रभाव एक अपवाद है। C # 4.0 संस्करण एक अधिक वर्णनात्मक अपवाद देता है।
डैनियल ईयरविकर

4
यदि आप स्रोत से भिन्न असेंबली में डायनामिक का उपयोग कर रहे हैं, तो आपको [InternalsVoubleTo] का उपयोग करने की आवश्यकता है
Sarath

2
@DanielEarwicker को पूरा करने के लिए धन्यवाद। यह अनाम प्रकारों पर भी लागू होता है। जैसे कि गुमनाम प्रकारों के लिए उत्पन्न सभी गुण आंतरिक हैं।
सारथ

13

आप रिफ्लेक्शन का उपयोग करके गुमनाम प्रकार के गुणों पर पुनरावृति कर सकते हैं; देखें कि क्या कोई "चेक की गई" संपत्ति है और अगर वहाँ है तो उसका मूल्य प्राप्त करें।

इस ब्लॉग पोस्ट को देखें: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx

तो कुछ इस तरह:

foreach(object o in nodes)
{
    Type t = o.GetType();

    PropertyInfo[] pi = t.GetProperties(); 

    foreach (PropertyInfo p in pi)
    {
        if (p.Name=="Checked" && !(bool)p.GetValue(o))
            Console.WriteLine("awesome!");
    }
}

6
यदि आपको केवल एक संपत्ति की आवश्यकता है और आप पहले से ही इसका नाम जानते हैं, तो उन सभी के माध्यम से जाने का कोई मतलब नहीं है; बस GetProperty और GetValue का उपयोग करें। इसके अलावा, System.out.println जावा है, न कि C # ...
क्रिस चारबरुक

उफ़, तो यह क्रिस है! थोड़ा शर्मनाक ... अब तय।
Glennkentwell

6

स्वीकृत उत्तर सही ढंग से बताता है कि सूची कैसे घोषित की जानी चाहिए और अधिकांश परिदृश्यों के लिए अत्यधिक अनुशंसित है।

लेकिन मैं एक अलग परिदृश्य में आया, जो पूछे गए प्रश्न को भी कवर करता है। यदि आपको किसी मौजूदा ऑब्जेक्ट सूची का उपयोग करना है, जैसे ViewData["htmlAttributes"]कि MVC में ? आप इसके गुणों का उपयोग कैसे कर सकते हैं (वे आमतौर पर के माध्यम से बनाए जाते हैंnew { @style="width: 100px", ... } )?

इस अलग परिदृश्य के लिए मैं आपके साथ साझा करना चाहता हूं जो मुझे पता चला। नीचे दिए गए समाधानों में, मैं निम्नलिखित घोषणा कर रहा हूं nodes:

List<object> nodes = new List<object>();

nodes.Add(
new
{
    Checked = false,
    depth = 1,
    id = "div_1" 
});

1. गतिशील के साथ समाधान

में सी # 4.0 और उच्चतर संस्करण, तो आप बस गतिशील और लिखने पर कास्ट कर सकते:

if (nodes.Any(n => ((dynamic)n).Checked == false))
    Console.WriteLine("found not checked element!");

नोट: यह उपयोग कर रहा है देर से बाध्यकारी, जिसका अर्थ है कि यह केवल रनटाइम पर पहचान लेंगे अगर वस्तु एक नहीं है Checkedसंपत्ति और एक फेंकता RuntimeBinderExceptionइस मामले में - इसलिए यदि आप एक गैर-मौजूद इस्तेमाल करने की कोशिश Checked2संपत्ति आपको निम्न संदेश मिलता है पर क्रम: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"

2. प्रतिबिंब के साथ समाधान

प्रतिबिंब के साथ समाधान पुराने और नए C # संकलक संस्करणों के साथ काम करता है । पुराने C # संस्करणों के लिए कृपया इस उत्तर के अंत में संकेत दें।

पृष्ठभूमि

शुरुआती बिंदु के रूप में, मुझे यहां एक अच्छा जवाब मिला । प्रतिबिंब के रूप में अनाम डेटा प्रकार को शब्दकोश में परिवर्तित करने का विचार है। शब्दकोश को गुणों को एक्सेस करना आसान बनाता है, क्योंकि उनके नाम कुंजी के रूप में संग्रहीत किए जाते हैं (आप उन्हें पसंद कर सकते हैंmyDict["myProperty"] )।

ऊपर दिए गए लिंक में कोड से प्रेरित होकर, मैंने एक विस्तार वर्ग प्रदान किया GetProp, UnanonymizePropertiesऔर UnanonymizeListItemsविस्तार विधियों के रूप में, जो गुमनाम संपत्तियों तक पहुंच को आसान बनाता है। इस वर्ग के साथ आप केवल निम्नानुसार क्वेरी कर सकते हैं:

if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
    Console.WriteLine("found not checked element!");
}

या आप स्थिति के nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()रूप में अभिव्यक्ति का उपयोग कर सकते ifहैं, जो अंतर्निहित रूप से फ़िल्टर करता है और फिर जांचता है कि क्या कोई तत्व वापस आए हैं या नहीं।

"चेक की गई" संपत्ति वाली पहली वस्तु प्राप्त करने और अपनी संपत्ति "गहराई" वापस करने के लिए, आप उपयोग कर सकते हैं:

var depth = nodes.UnanonymizeListItems()
             ?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");

या इससे कम: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];

नोट: यदि आपके पास उन वस्तुओं की एक सूची है, जिनमें आवश्यक रूप से सभी गुण नहीं हैं (उदाहरण के लिए, कुछ में "चेक की गई संपत्ति" नहीं है), और आप अभी भी "चेक किए गए" मानों के आधार पर एक क्वेरी बनाना चाहते हैं, तो आप कर सकते हैं यह करो:

if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true)); 
                                      return y.HasValue && y.Value == false;}).Any())
{
    Console.WriteLine("found not checked element!");
}

यह रोकता है, कि KeyNotFoundExceptionअगर "चेक की गई" संपत्ति मौजूद नहीं है।


नीचे दिए गए वर्ग में निम्नलिखित विस्तार विधियाँ शामिल हैं:

  • UnanonymizeProperties: किसी वस्तु में निहित गुणों को डी-अनॉइज़ करने के लिए उपयोग किया जाता है । यह विधि प्रतिबिंब का उपयोग करती है। यह ऑब्जेक्ट को गुण और उसके मूल्यों वाले शब्दकोश में परिवर्तित करता है।
  • UnanonymizeListItems: वस्तुओं की सूची को गुणों से युक्त शब्दकोशों की सूची में बदलने के लिए उपयोग किया जाता है। यह वैकल्पिक रूप से पहले से छानने के लिए एक लंबोदर अभिव्यक्ति हो सकती है ।
  • GetProp: दिए गए संपत्ति के नाम से मेल खाते एक एकल मान को वापस करने के लिए उपयोग किया जाता है। KeyNotFoundException (झूठी) के बजाय शून्य मानों (सत्य) के रूप में मौजूदा-मौजूदा गुणों का इलाज करने की अनुमति देता है

उपरोक्त उदाहरणों के लिए, सभी आवश्यक है कि आप नीचे विस्तार वर्ग जोड़ दें:

public static class AnonymousTypeExtensions
{
    // makes properties of object accessible 
    public static IDictionary UnanonymizeProperties(this object obj)
    {
        Type type = obj?.GetType();
        var properties = type?.GetProperties()
               ?.Select(n => n.Name)
               ?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj, null));
        return properties;
    }

    // converts object list into list of properties that meet the filterCriteria
    public static List<IDictionary> UnanonymizeListItems(this List<object> objectList, 
                    Func<IDictionary<string, object>, bool> filterCriteria=default)
    {
        var accessibleList = new List<IDictionary>();
        foreach (object obj in objectList)
        {
            var props = obj.UnanonymizeProperties();
            if (filterCriteria == default
               || filterCriteria((IDictionary<string, object>)props) == true)
            { accessibleList.Add(props); }
        }
        return accessibleList;
    }

    // returns specific property, i.e. obj.GetProp(propertyName)
    // requires prior usage of AccessListItems and selection of one element, because
    // object needs to be a IDictionary<string, object>
    public static object GetProp(this object obj, string propertyName, 
                                 bool treatNotFoundAsNull = false)
    {
        try 
        {
            return ((System.Collections.Generic.IDictionary<string, object>)obj)
                   ?[propertyName];
        }
        catch (KeyNotFoundException)
        {
            if (treatNotFoundAsNull) return default(object); else throw;
        }
    }
}

सुझाव: कोड ऊपर उपयोग कर रहा है अशक्त-सशर्त ऑपरेटर, के बाद से सी # संस्करण 6.0 उपलब्ध है - अगर आप के साथ हैं काम कर रहे बड़े सी # संकलक (जैसे सी # 3.0), बस की जगह ?.द्वारा .और ?[द्वारा [, हर जगह जैसे

var depth = nodes.UnanonymizeListItems()
            .FirstOrDefault(n => n.Contains("Checked"))["depth"];

यदि आप पुराने C # कंपाइलर का उपयोग करने के लिए बाध्य नहीं हैं , तो इसे वैसे ही रखें, क्योंकि अशक्त-स्थिति का उपयोग करने से अशक्तता बहुत आसान हो जाती है।

नोट: डायनामिक के साथ अन्य समाधान की तरह, यह समाधान भी देर से बाध्यकारी का उपयोग कर रहा है, लेकिन इस मामले में आपको एक अपवाद नहीं मिल रहा है - यह तत्व नहीं मिलेगा यदि आप एक गैर-मौजूदा संपत्ति की बात कर रहे हैं, जब तक कि जैसा कि आप अशक्त संचालकों को रखते हैं।

कुछ अनुप्रयोगों के लिए क्या उपयोगी हो सकता है कि संपत्ति को समाधान 2 में स्ट्रिंग के माध्यम से संदर्भित किया जाता है, इसलिए इसे पैरामीटर किया जा सकता है।


1

हाल ही में, मुझे .NET 3.5 (कोई डायनामिक उपलब्ध नहीं) के भीतर भी यही समस्या थी। यहाँ है कि मैं कैसे हल:

// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };

using (frmFind f = new frmFind(args)) 
{
...
...
}

Stackoverflow पर कहीं से अनुकूलित:

// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
   return (T)x;
}

अब कास्ट के माध्यम से वस्तु वापस प्राप्त करें:

public partial class frmFind: Form
{
    public frmFind(object arguments)
    {

        InitializeComponent();

        var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });

        this.Text = args.Title;

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