2010-04-07 11 views
50

मैं किसी अज्ञात ऑब्जेक्ट को किसी विधि के लिए तर्क के रूप में लेना चाहता हूं, और उसके बाद प्रत्येक गुण/मान को गतिशील ExpandoObject में जोड़ने के लिए अपनी गुणों को फिर से चालू करना चाहता हूं।मैं सी # में किसी अज्ञात ऑब्जेक्ट के गुणों पर कैसे पुन: प्रयास करूं?

तो क्या मैं जरूरत से

new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 } 

नाम और प्रत्येक संपत्ति के मूल्यों को जानने, और उन्हें ExpandoObject में जोड़ने के लिए सक्षम होने के लिए जाने के लिए है।

मैं इसे कैसे पूरा करूं?

साइड नोट: यह मेरा इकाई परीक्षण के कई में किया जाएगा (मैं इसे उपयोग कर रहा हूँ सेटअप में कबाड़ का एक बहुत दूर refactor करने के लिए), इसलिए प्रदर्शन कुछ हद तक प्रासंगिक है। मैं प्रतिबिंब के बारे में यकीन है कि पर्याप्त के लिए कहने के लिए पता नहीं है, लेकिन मैं क्या समझ लिया है से यह बहुत प्रदर्शन भारी है, इसलिए यदि यह संभव है मैं नहीं बल्कि यह से बचने हूँ ...

फ़ॉलो-अप प्रश्न: जैसा कि मैंने कहा, मैं इस अज्ञात वस्तु को एक विधि के लिए तर्क के रूप में ले रहा हूं। विधि के हस्ताक्षर में मुझे किस डेटाटाइप का उपयोग करना चाहिए? यदि मैं object का उपयोग करता हूं तो क्या सभी गुण उपलब्ध होंगे?

+1

प्रतिबिंब प्रदर्शन वास्तव में बहुत भयानक नहीं है। यदि आपके पास बहुत बड़ी संख्या है, तो आपको इसे करने की आवश्यकता है, आप किसी दिए गए अनाम प्रकार के लिए PropertyInfo प्रविष्टियों को कैश कर सकते हैं और फिर प्रत्येक प्रॉपर्टी के गुणों को हल करने के लिए उन PropertyInfo प्रविष्टियों पर दोहरा सकते हैं। आप प्रत्येक प्रॉपर्टी के लिए GetMethod के लिए प्रतिनिधि भी बना सकते हैं और उनको कैश कर सकते हैं। –

उत्तर

60
foreach(var prop in myVar.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) 
{ 
    Console.WriteLine("Name: {0}, Value: {1}",prop.Name, prop.GetValue(myVar,null)); 
} 
+0

ठीक है, मुझे प्रतिबिंब का उपयोग करना है। क्या यह एक प्रदर्शन मुद्दा होगा? साथ ही, मैं प्रॉपर्टी का नाम किसी रूप में कैसे प्राप्त करूं जिसका उपयोग मैं ExpandoObject में जोड़ने के लिए कर सकता हूं? –

+1

मैंने अनुवर्ती प्रश्न की समस्या हल की: '(MyExpandoObject आईसीओलेक्शन >) के रूप में। जोड़ें (...)' चाल चल रही है। धन्यवाद! –

+0

सिद्धांत रूप में, ऐसा करने के लिए प्रतिबिंब की आवश्यकता नहीं होनी चाहिए। यदि एक संकलन-समय लूपिंग सुविधा प्रदान की गई थी, तो वही परिणाम अधिक कुशलता से प्राप्त किया जा सकता था, और आत्मनिरीक्षण संकलन-समय तक ही सीमित हो सकता है। – pyon

0

आप प्रतिबिंब का उपयोग करना होगा .... (code "borrowed" from this url)

using System.Reflection; // reflection namespace 

// get all public static properties of MyClass type 
PropertyInfo[] propertyInfos; 
propertyInfos = typeof(MyClass).GetProperties(BindingFlags.Public | 
               BindingFlags.Static); 
// sort properties by name 
Array.Sort(propertyInfos, 
     delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2) 
     { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); }); 

// write property names 
foreach (PropertyInfo propertyInfo in propertyInfos) 
{ 
    Console.WriteLine(propertyInfo.Name); 
} 
+0

ठीक है - मुझे प्रतिबिंब का उपयोग करना है। यदि यह परीक्षण के बाद एक बार मेरे अधिकांश यूनिट परीक्षणों में ऐसा करता है तो यह एक प्रदर्शन मुद्दा होगा? –

+1

@ टॉमस: उस जानकारी के साथ उस तरह के प्रश्न का उत्तर देने का कोई तरीका नहीं है। बस कोशिश करो और देखें। –

1

उपयोग Reflection.Emit एक सामान्य विधि एक ExpandoObject को भरने के लिए बनाने के लिए।

या उपयोग भाव शायद (मुझे लगता है कि यह केवल हालांकि नेट 4 में संभव हो जाएगा)। , जब लागू केवल एक प्रतिनिधि (जो स्पष्ट रूप से कैश हो जाने की जरूरत है) की स्थापना के दौरान

न तो इन तरीकों का प्रतिबिंब का उपयोग करता है।

यहाँ एक शब्दकोश (मुझे लगता है कि ExpandoObject दूर नहीं है) को भरने के लिए कुछ Reflection.Emit कोड है,

static T CreateDelegate<T>(this DynamicMethod dm) where T : class 
{ 
    return dm.CreateDelegate(typeof(T)) as T; 
} 

static Dictionary<Type, Func<object, Dictionary<string, object>>> cache = 
    new Dictionary<Type, Func<object, Dictionary<string, object>>>(); 

static Dictionary<string, object> GetProperties(object o) 
{ 
    var t = o.GetType(); 

    Func<object, Dictionary<string, object>> getter; 

    if (!cache.TryGetValue(t, out getter)) 
    { 
    var rettype = typeof(Dictionary<string, object>); 

    var dm = new DynamicMethod(t.Name + ":GetProperties", rettype, 
     new Type[] { typeof(object) }, t); 

    var ilgen = dm.GetILGenerator(); 

    var instance = ilgen.DeclareLocal(t); 
    var dict = ilgen.DeclareLocal(rettype); 

    ilgen.Emit(OpCodes.Ldarg_0); 
    ilgen.Emit(OpCodes.Castclass, t); 
    ilgen.Emit(OpCodes.Stloc, instance); 

    ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes)); 
    ilgen.Emit(OpCodes.Stloc, dict); 

    var add = rettype.GetMethod("Add"); 

    foreach (var prop in t.GetProperties(
     BindingFlags.Instance | 
     BindingFlags.Public)) 
    { 
     ilgen.Emit(OpCodes.Ldloc, dict); 

     ilgen.Emit(OpCodes.Ldstr, prop.Name); 

     ilgen.Emit(OpCodes.Ldloc, instance); 
     ilgen.Emit(OpCodes.Ldfld, prop); 
     ilgen.Emit(OpCodes.Castclass, typeof(object)); 

     ilgen.Emit(OpCodes.Callvirt, add); 
    } 

    ilgen.Emit(OpCodes.Ldloc, dict); 
    ilgen.Emit(OpCodes.Ret); 

    cache[t] = getter = 
     dm.CreateDelegate<Func<object, Dictionary<string, object>>>(); 
    } 

    return getter(o); 
} 
3

एक वैकल्पिक दृष्टिकोण ExpandoObject के बजाय DynamicObject उपयोग करने के लिए है, और कहा कि जिस तरह से आप केवल प्रतिबिंब करने का भूमि के ऊपर है, तो आप वास्तव में अन्य वस्तु से एक संपत्ति तक पहुँचने का प्रयास।

public class DynamicForwarder : DynamicObject 
{ 
    private object _target; 

    public DynamicForwarder(object target) 
    { 
     _target = target; 
    } 

    public override bool TryGetMember(
     GetMemberBinder binder, out object result) 
    { 
     var prop = _target.GetType().GetProperty(binder.Name); 
     if (prop == null) 
     { 
      result = null; 
      return false; 
     } 

     result = prop.GetValue(_target, null); 
     return true; 
    } 
} 

अब जब आप वास्तव में एक गतिशील प्राप्त के माध्यम से संपत्ति तक पहुंचने का प्रयास करते हैं तो यह केवल प्रतिबिंब करता है। नकारात्मक पक्ष पर, यदि आप बार-बार उसी संपत्ति तक पहुंचते हैं, तो इसे हर बार प्रतिबिंब करना पड़ता है। तो तुम परिणाम कैश सकता है:

public class DynamicForwarder : DynamicObject 
{ 
    private object _target; 
    private Dictionary<string, object> _cache = new Dictionary<string, object>(); 

    public DynamicForwarder(object target) 
    { 
     _target = target; 
    } 

    public override bool TryGetMember(
     GetMemberBinder binder, out object result) 
    { 
     // check the cache first 
     if (_cache.TryGetValue(binder.Name, out result)) 
      return true; 

     var prop = _target.GetType().GetProperty(binder.Name); 
     if (prop == null) 
     { 
      result = null; 
      return false; 
     } 

     result = prop.GetValue(_target, null); 
     _cache.Add(binder.Name, result); // <-------- insert into cache 
     return true; 
    } 
} 

आप उनके गुणों सम्मिलित करने लक्ष्य वस्तुओं की एक सूची के भंडारण, और समर्थन की स्थापना गुण (एक समान ओवरराइड साथ TrySetMember कहा जाता है) का समर्थन कर सकता है आप गतिशील रूप से कैश में मूल्यों को निर्धारित करने की अनुमति शब्दकोश।

बेशक

, प्रतिबिंब की भूमि के ऊपर शायद के बारे में चिंता के लायक होने के लिए नहीं जा रहा है, लेकिन बड़ी वस्तुओं के लिए यह यह के प्रभाव को सीमित कर सकता है। इससे अधिक दिलचस्प क्या है यह अतिरिक्त लचीलापन है जो आपको देता है।

6

गुमनाम वस्तु अपनी संपत्ति के नाम और मान पाने के लिए पर प्रतिबिंबित, तो एक ExpandoObject वास्तव में एक शब्दकोश यह पॉप्युलेट करने के लिए किया जा रहा है का लाभ लेने के।यहाँ एक उदाहरण है, एक इकाई परीक्षण के रूप में व्यक्त:

[TestMethod] 
    public void ShouldBeAbleToConvertAnAnonymousObjectToAnExpandoObject() 
    { 
     var additionalViewData = new {id = "myControlId", css = "hide well"}; 
     dynamic result = new ExpandoObject(); 
     var dict = (IDictionary<string, object>)result; 
     foreach (PropertyInfo propertyInfo in additionalViewData.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) 
     { 
      dict[propertyInfo.Name] = propertyInfo.GetValue(additionalViewData, null); 
     } 
     Assert.AreEqual(result.id, "myControlId"); 
     Assert.AreEqual(result.css, "hide well"); 
    } 
2

यह एक पुरानी सवाल यह है, लेकिन अब आप निम्न कोड के साथ यह करने के लिए सक्षम होना चाहिए:

dynamic expObj = new ExpandoObject(); 
    expObj.Name = "James Kirk"; 
    expObj.Number = 34; 

// print the dynamically added properties 
// enumerating over it exposes the Properties and Values as a KeyValuePair 
foreach (KeyValuePair<string, object> kvp in expObj){ 
    Console.WriteLine("{0} = {1} : Type: {2}", kvp.Key, kvp.Value, kvp.Value.GetType()); 
} 

उत्पादन कैसा लगेगा निम्नलिखित:

नाम = जेम्स किर्क: प्रकार: System.String

संख्या = 34: प्रकार: सिस्टम .t32

संबंधित मुद्दे

 संबंधित मुद्दे