C # 'डायनामिक' अन्य विधानसभा में घोषित गुमनाम प्रकारों से संपत्तियों तक नहीं पहुंच सकता है


87

नीचे दिए गए कोड तब तक अच्छी तरह से काम कर रहे हैं जब तक कि मेरे पास क्लास ClassSameAssemblyके समान क्लास में क्लास है Program। लेकिन जब मैं कक्षा ClassSameAssemblyको एक अलग विधानसभा में ले जाता हूं , तो RuntimeBinderException(नीचे देखें) फेंक दिया जाता है। क्या इसे हल करना संभव है?

using System;

namespace ConsoleApplication2
{
    public static class ClassSameAssembly
    {
        public static dynamic GetValues()
        {
            return new
            {
                Name = "Michael", Age = 20
            };
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var d = ClassSameAssembly.GetValues();
            Console.WriteLine("{0} is {1} years old", d.Name, d.Age);
        }
    }
}

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'ऑब्जेक्ट' में 'नाम' की परिभाषा नहीं है

at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at ConsoleApplication2.Program.Main(String[] args) in C:\temp\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 23

StackTrace: CallSite.Target पर (सिस्टम बंद, CallSite, ऑब्जेक्ट) System.Dynamic.UpdateDelegates.UpdateAndExecute1 [T0, TRet] (कॉलसाइट साइट, T0 arg0) ConsoleApplication2.Program.Main (String]] पर। \ परियोजनाओं \ ConsoleApplication2 \ ConsoleApplication2 \ Program.cs: System.AppDomain._nExecuteAssembly (RuntimeAssembly विधानसभा, स्ट्रिंग [] args) के लिए System.AppDomain.nExecuteAssembly (RuntimeAssembly विधानसभा, String []] []] स्ट्रिंग
असेंबलीफ़ाइल

Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly () पर System.Threading.TelreadHelper.ThreadStart_Context (वस्तु स्थिति) System.Threading .ExecutionContext.Run (एक्ज़ीक्यूशन कॉन्टेक्स्ट कॉन्टेक्स्ट), कॉन्टेक्स्ट कॉलबैक। ExecutionContext.Run (ExecutionContext execContext, ContextCallback callback, Object state) System.Threading.ThreadHelper.ThreadStart ()
InnerException पर

पूर्ण स्रोत कोड के साथ कोई अंतिम समाधान?
कीकेनेट

जवाबों:


116

मेरा मानना ​​है कि समस्या यह है कि अनाम प्रकार के रूप में उत्पन्न होता है internal, इसलिए बांधने वाला वास्तव में इस तरह के बारे में "पता" नहीं करता है।

इसके बजाय ExpandoObject का उपयोग करने का प्रयास करें:

public static dynamic GetValues()
{
    dynamic expando = new ExpandoObject();
    expando.Name = "Michael";
    expando.Age = 20;
    return expando;
}

मुझे पता है कि यह कुछ हद तक बदसूरत है, लेकिन यह सबसे अच्छा है जो मैं इस समय सोच सकता हूं ... मुझे नहीं लगता कि आप इसके साथ एक ऑब्जेक्ट इनिशलाइज़र का उपयोग भी कर सकते हैं, क्योंकि यह दृढ़ता से टाइप किया ExpandoObjectगया है क्योंकि कंपाइलर को पता नहीं होगा कि क्या करना है "नाम" और "आयु" के साथ। आप ऐसा करने में सक्षम हो सकते हैं:

 dynamic expando = new ExpandoObject()
 {
     { "Name", "Michael" },
     { "Age", 20 }
 };
 return expando;

लेकिन यह बहुत बेहतर नहीं है ...

आप संभावित रूप से प्रतिबिंब के माध्यम से उसी प्रकार की सामग्री के साथ एक अनाम प्रकार को विस्तारक में बदलने के लिए एक विस्तार विधि लिख सकते हैं। तब आप लिख सकते हैं:

return new { Name = "Michael", Age = 20 }.ToExpando();

हालांकि यह बहुत भयानक है :(


1
धन्यवाद जॉन। मुझे बस एक वर्ग का उपयोग करने में एक ही समस्या थी जो विधानसभा के लिए निजी थी।
डेव मार्कल

2
मैं अंत में अपने भयानक उदाहरण की तरह कुछ प्यार करता हूँ, बस के रूप में भयानक नहीं है। उपयोग करने के लिए: डायनेमिक प्रॉप्स = नया {मेटाडेटा = डिटेलमोडेलमेटाटा। क्रिएट, पेजटाइट = "न्यू कंटेंट", पेजहेडिंग = "कंटेंट मैनेजमेंट"}; और नामित सदस्यों को डायनामिक सदस्यों के रूप में जोड़ा गया है जो बहुत अच्छा होगा!
ProfK

ओपन सोर्स फ्रेमवर्क इंप्रोप्टु इंटरफ़ेस बहुत कुछ करता है dlr के साथ इसमें इनलाइन इनिशियलाइज़ेशन सिंटैक्स होता है जो किसी भी ऑब्जेक्ट को डायनामिक या स्टैटिक के लिए काम करता है। return Build<ExpandoObject>.NewObject(Name:"Micheal", Age: 20);
jbtule

1
अनाम प्रकार को विस्तारक में बदलने के लिए एक्सटेंशन विधि के लिए कोई भी पूर्ण स्रोत कोड नमूना?
कीकेनेट

1
@ Md.lbrahim: आप मूल रूप से नहीं कर सकते। आपको इसे या तो objectजेनेरिक प्रकार पर करना होगा (आप आवश्यकता कर सकते हैं कि यह एक वर्ग है ...) और निष्पादन समय पर प्रकार की जांच करें।
जॉन स्कीट

63

आप का उपयोग [assembly: InternalsVisibleTo("YourAssemblyName")]कर सकते हैं आप विधानसभा आंतरिक दिखाई देने के लिए।


2
जॉन का जवाब अधिक पूर्ण है, लेकिन यह वास्तव में मेरे लिए एक सरल सरल समाधान प्रदान करता है। धन्यवाद :)
केलोटी

मैं अलग-अलग मंचों पर घंटों तक अपना सिर पीटता रहा, लेकिन इसके सिवा कोई सरल जवाब नहीं मिला। धन्यवाद ल्यूक। लेकिन फिर भी मैं यह नहीं समझ पा रहा हूं कि एक विधानसभा के बाहर एक गतिशील प्रकार क्यों सुलभ नहीं है, जैसा कि एक ही विधानसभा में होता है? मेरा मतलब है कि .Net में यह प्रतिबंध क्यों है।
फैसल मक सेप

@FaisalMq यह इसलिए है क्योंकि गुमनाम कक्षाओं को उत्पन्न करने वाले संकलक उन्हें "आंतरिक" घोषित करते हैं। पता नहीं कौन सा असली कारण है।
ema

2
हां, मुझे लगता है कि यह उत्तर महत्वपूर्ण है, क्योंकि मैं वर्किंग कोड को बदलना नहीं चाहता, मुझे सिर्फ एक और विधानसभा से इसका परीक्षण करने की आवश्यकता है
पांडावुड

यहां जोड़ने के लिए एक नोट यह है कि इस कार्य के लिए इस परिवर्तन के बाद आपको Visual Studio को पुनरारंभ करना होगा।
रेडी

11

मैं एक similair समस्या में भाग गया और जॉन स्कीट्स के जवाब में जोड़ना चाहूंगा कि एक और विकल्प है। इसका कारण मुझे पता चला कि मुझे एहसास हुआ कि एस्प एमवीसी 3 में कई विस्तार विधियाँ html विशेषताएँ प्रदान करने के लिए इनपुट के रूप में अनाम कक्षाओं का उपयोग करती हैं (नई {alt = "छवि ऊंचाई", शैली = "पैडिंग-टॉप: 5 पीएक्स"}} =>

वैसे भी - उन कार्यों में रूटवैल्यूडर क्लास के कंस्ट्रक्टर का उपयोग किया जाता है। मैंने खुद कोशिश की, और निश्चित रूप से यह काम करता है - हालांकि केवल पहला स्तर (मैंने एक बहु-स्तरीय संरचना का उपयोग किया है)। एसओ - कोड में यह होगा:

object o = new {
    name = "theName",
    props = new {
        p1 = "prop1",
        p2 = "prop2"
    }
}
SeparateAssembly.TextFunc(o)

//In SeparateAssembly:
public void TextFunc(Object o) {
  var rvd = new RouteValueDictionary(o);

//Does not work:
Console.WriteLine(o.name);
Console.WriteLine(o.props.p1);

//DOES work!
Console.WriteLine(rvd["name"]);

//Does not work
Console.WriteLine(rvd["props"].p1);
Console.WriteLine(rvd["props"]["p1"]);

तो ... वास्तव में यहाँ क्या हो रहा है? रूटवैल्यूडॉर के अंदर एक झांकने से इस कोड (मान ~ = o ऊपर) का पता चलता है:

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
    object obj2 = descriptor.GetValue(values);
    //"this.Add" would of course need to be adapted
    this.Add(descriptor.Name, obj2);
}

SO - TypeDescriptor.GetProperties (o) का उपयोग करके हम एक अलग असेंबली में आंतरिक के रूप में निर्मित किए जा रहे अनाम प्रकार के बावजूद गुण और मान प्राप्त कर सकेंगे! और निश्चित रूप से इसे पुनरावर्ती बनाने के लिए विस्तार करना काफी आसान होगा। और यदि आप चाहते हैं तो एक विस्तार विधि बनाने के लिए।

उम्मीद है की यह मदद करेगा!

/ विक्टर


उस भ्रम के लिए क्षमा करें। प्रोप १ => पी १ से कोड अपडेट किया गया जहाँ उपयुक्त हो। फिर भी - पूरी पोस्ट के साथ विचार टाइप करने के लिए टाइप करने के लिए था TypeDescriptor.GetProperties समस्या को हल करने के लिए एक विकल्प के रूप में, जो उम्मीद है कि वैसे भी स्पष्ट था ...
विक्टर

यह वास्तव में बेवकूफ है कि गतिशील हमारे लिए ऐसा नहीं कर सकता है। मैं दोनों वास्तव में प्यार करता हूं और वास्तव में गतिशील से नफरत करता हूं।
क्रिस मैरिसिक

2

यहाँ ToExpandoObject के लिए एक विस्तार विधि का एक अल्पविकसित संस्करण है कि मुझे यकीन है कि पॉलिश करने के लिए जगह है।

    public static ExpandoObject ToExpandoObject(this object value)
    {
        // Throw is a helper in my project, replace with your own check(s)
        Throw<ArgumentNullException>.If(value, Predicates.IsNull, "value");

        var obj = new ExpandoObject() as IDictionary<string, object>;

        foreach (var property in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            obj.Add(property.Name, property.GetValue(value, null));
        }

        return obj as ExpandoObject;
    }

    [TestCase(1, "str", 10.75, 9.000989, true)]
    public void ToExpandoObjectTests(int int1, string str1, decimal dec1, double dbl1, bool bl1)
    {
        DateTime now = DateTime.Now;

        dynamic value = new {Int = int1, String = str1, Decimal = dec1, Double = dbl1, Bool = bl1, Now = now}.ToExpandoObject();

        Assert.AreEqual(int1, value.Int);
        Assert.AreEqual(str1, value.String);
        Assert.AreEqual(dec1, value.Decimal);
        Assert.AreEqual(dbl1, value.Double);
        Assert.AreEqual(bl1, value.Bool);
        Assert.AreEqual(now, value.Now);
    }

1

एक क्लीनर समाधान होगा:

var d = ClassSameAssembly.GetValues().ToDynamic();

जो अब एक ExpandoObject है।

संदर्भ के लिए याद रखें:

Microsoft.CSharp.dll

1

नीचे दिए गए समाधान ने मेरे कंसोल एप्लिकेशन प्रोजेक्ट्स में मेरे लिए काम किया

इस [असेंबली: इंटरनैशनलविजेंटलो ("YourAssemblyName")] को अलग-अलग प्रोजेक्ट के in \ गुण \ फंक्शन में डायनामिक ऑब्जेक्ट लौटाते हुए डालें।

"YourAssemblyName" कॉलिंग प्रोजेक्ट का असेंबली नाम है। आप असेंबली के माध्यम से प्राप्त कर सकते हैं। GetExecutingAssembly ()। फुलनाम को कॉलिंग प्रोजेक्ट में निष्पादित करके।


0

बहादुर लोगों के लिए ToExpando विस्तार विधि (जॉन के जवाब में उल्लेख किया गया है)

public static class ExtensionMethods
{
    public static ExpandoObject ToExpando(this object obj)
    {
        IDictionary<string, object> expando = new ExpandoObject();
        foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(obj))
        {
            var value = propertyDescriptor.GetValue(obj);
            expando.Add(propertyDescriptor.Name, value == null || new[]
            {
                typeof (Enum),
                typeof (String),
                typeof (Char),
                typeof (Guid),
                typeof (Boolean),
                typeof (Byte),
                typeof (Int16),
                typeof (Int32),
                typeof (Int64),
                typeof (Single),
                typeof (Double),
                typeof (Decimal),
                typeof (SByte),
                typeof (UInt16),
                typeof (UInt32),
                typeof (UInt64),
                typeof (DateTime),
                typeof (DateTimeOffset),
                typeof (TimeSpan),
            }.Any(oo => oo.IsInstanceOfType(value))
                ? value
                : value.ToExpando());
        }

        return (ExpandoObject)expando;
    }
}

0

यदि आप पहले से ही अपने प्रोजेक्ट में Newtonsoft.Json का उपयोग कर रहे हैं (या आप इसे इस उद्देश्य के लिए जोड़ना चाहते हैं), तो आप उस भयानक एक्सटेंशन विधि को लागू कर सकते हैं जो जॉन स्कीट इस तरह से अपने उत्तर में कर रहा है :

public static class ObjectExtensions
{
    public static ExpandoObject ToExpando(this object obj)
        => JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj));
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.