2010-03-01 12 views
8

मैं जोड़े स्ट्रिंग, ऑब्जेक्ट के सेट से कुछ प्रकार टी के ऑब्जेक्ट को तुरंत चालू करने के लिए अधिक सामान्य/"मानक" तरीका ढूंढ रहा हूं। मेरे लिए ऐसा लगता है कि ऐसा करने के लिए कुछ प्रसिद्ध तरीका होना चाहिए लेकिन मुझे यह नहीं मिल रहा है, इसलिए मैं कोड के इस टुकड़े के साथ आया हूं। क्या कोई बेहतर कुछ जानता है? शब्दकोश से टाइप टी के किसी ऑब्जेक्ट को प्रारंभ करें <string, object>

// usage 
public class test 
    { 
    public int field1; 
    public string field2; 
    public bool field3; 
    public string[] field4; 
    public IDictionary<string,object> field5 { get; set; } 

    public static IDictionary<string,object> dynamic() 
     { 
     return new Dictionary<string,object>{ 
      { "field1", 2 }, 
      { "field2", "string" }, 
      { "field3", true }, 
      { "field4", new[] { "id3", "id4", "id5" } }, 
      { "field5", new Dictionary<string,object>{ { "id1", "" } } } 
      }; 
     } 
    } 

... 
var r = new dynamic_data_serializer<test>().create(test.dynamic()); 
... 

// 
public class dynamic_data_serializer<T> 
    { 
    public T create(object obj) 
     { 
     var result = default(T); 
     if (obj == null) 
      return result; 

     var ttype = typeof(T); 
     var objtype = obj.GetType(); 
     if (ttype.IsAssignableFrom(objtype)) { 
      result = (T)obj; 
      return result; 
      } 

     if (ttype.IsClass) { // custom classes, array, dictionary, etc. 
      result = Activator.CreateInstance<T>(); 

      if (objtype == typeof(IDictionary<string,object>) || 
        objtype == typeof(Dictionary<string,object>)) { 
       var obj_as_dict = obj as IDictionary<string,object>; 
       var fields = ttype.GetFields(); 
       if (fields.Length > 0) 
        set_fields_from(result, fields, obj_as_dict); 

       var properties = ttype.GetProperties(); 
       if (properties.Length > 0) 
        set_properties_from(result, properties, obj_as_dict); 
       } 
      }  
     return result; 
     } 

    private void set_fields_from(T _this_, FieldInfo[] fields, IDictionary<string,object> obj) { 
     foreach (var fld in fields) { 
      var v = find(obj, fld.Name); 
      if (v != null) { 
       var mobj = call_deserialize(fld.FieldType, v); 
       fld.SetValue(_this_, mobj); 
       } 
      } 
     } 

    private void set_properties_from(T _this_, PropertyInfo[] properties, IDictionary<string,object> obj) { 
     foreach (var prop in properties) { 
      var v = find(obj, prop.Name); 
      if (v != null) { 
       var mobj = call_deserialize(prop.PropertyType, v); 
       prop.SetValue(_this_, mobj, null); 
       } 
      } 
     } 

    private object find(IDictionary<string,object> obj, string name) { 
     foreach (var kv in obj) 
      if (string.Compare(kv.Key, name, true) == 0) 
       return kv.Value; 
     return null; 
     } 

    private object call_deserialize(Type des_type, object value) { 
     var gtype = typeof(dynamic_data_serializer<>); 
     Type desz_type = gtype.MakeGenericType(new[]{ des_type }); 
     object desz = Activator.CreateInstance(desz_type); 
     var method_type = desz_type.GetMethod("create"); 
     return method_type.Invoke(desz, new[]{ value }); 
     } 
    } 
} 

उत्तर

2

DataContractJsonSerializer बहुत धीमा है, लेकिन आप प्रतिबिंब का उपयोग कर रहे हैं? यदि आपको बहुत सारी वस्तुओं को deserialize करना है, तो मैं प्रतिबिंब के बजाय संकलित lambdas का उपयोग करने की सिफारिश करेंगे। एक लैम्ब्डा केवल गुणों को सेट कर सकता है, फ़ील्ड नहीं (कम से कम .NET 3.5 में), इसलिए आपको उन कक्षाओं को समायोजित करना पड़ सकता है जिन्हें आप इसका उपयोग करते हैं, लेकिन यह इसके लायक है क्योंकि यह 1000 गुना तेज है।

यहाँ एक समारोह है कि एक संपत्ति सेटर स्थापित करने के लिए एक प्रकार और संपत्ति के लिए एक PropertyInfo दी बनाता है:

static Action<object, TValue> MakeSetter<TValue>(Type tclass, PropertyInfo propInfo) 
    { 
     var t = lambda.Expression.Parameter(typeof(object), "t"); 
     var v = lambda.Expression.Parameter(typeof(TValue), "v"); 
     // return (t, v) => ((tclass)t).prop = (tproperty)v 
     return (Action<object, TValue>) 
      lambda.Expression.Lambda(
       lambda.Expression.Call(
        lambda.Expression.Convert(t, tclass), 
        propInfo.GetSetMethod(), 
        lambda.Expression.Convert(v, propInfo.PropertyType)), 
       t, 
       v) 
      .Compile(); 
    } 

आप प्रत्येक वर्ग के लिए setters के एक शब्दकोश है, और यदि आप एक संपत्ति सेट करने के लिए जब भी एक वर्ग के, आप शब्दकोश में उस संपत्ति के लिए सेटटर को देखेंगे और इसे असाइन करने के लिए मूल्य के साथ कॉल करेंगे, जैसे: setters[propName](_this_, value);

+0

धन्यवाद! मैं इसका इस्तेमाल करने की कोशिश करूंगा। प्रतिबिंब का उपयोग करने के मामले में, सभी प्रकार की जानकारी कैश की जा सकती है। –

0

DataContractJsonSerializer

आप एक कस्टम serializer क्यों बनाना होगा और DataContractJsonSerializer का उपयोग नहीं?

संपादित

DataContractJsonSerializer आप फिट नहीं करता है, तो आप JSON.Net कोशिश कर सकते हैं। एक सीरिएलाइज़र को कुशलतापूर्वक कार्यान्वित करना एक आसान काम नहीं है, इसमें कई नुकसान और विशेष मामले हैं जिन्हें आप शामिल नहीं करना चाहते हैं। वैसे, आपका कोड नमूना प्रतिबिंब का भारी उपयोग करता है जो धीमा है, मुझे संदेह है कि यह DataContractJsonSerializer या JSON.Net से बेहतर प्रदर्शन करेगा।

+0

DataContractJsonSerializer सामान्य रूप में धीमा है और मेरे मामले में यह का उपयोग कर प्रदर्शन समस्या होती है। –

+0

धन्यवाद जेफ। मैं JSON.Net को देखूंगा। मैं मानता हूं, प्रतिबिंब धीमा है, लेकिन मैं सभी प्रकार की जानकारी कैश कर सकता हूं। –

+0

भले ही आप कैश टाइप करें, एक्टिवेटर का उपयोग करें। क्रिएटइंस्टेंस और फील्डइन्फो। नेटवैल्यू अभी भी धीमा है। चीजों को वास्तव में गति देने के लिए आपको Expression.Compile, Open delegates या reflection.emit जैसी चीजों का उपयोग करना होगा। कुंजी प्रति प्रकार केवल एक बार प्रतिबिंब का उपयोग करना है। –

1

मैं FormatterServices.PopulateObjectMembers का सुझाव दे सकता हूं, सिवाय इसके कि: यह अभी भी धीमी है AFAIK, और बी: मैंने कोशिश की (नीचे) और ऐसा लगता है कि संपत्ति पर अपवाद फेंकना है (डॉन क्यों नहीं पता; बहुत गहरा नहीं देखा)। एक और विकल्प Expression हो सकता है, लेकिन आप वास्तव में Compile प्रत्येक बार नहीं करना चाहते हैं (इसे केवल एक बार करने के लिए बेहतर है और इसे कैश करें, लेकिन यह एक ज्ञात प्रारूप की मांग करता है)।

public T create(object obj) 
{ // simplified for illustration 
    var bindings = obj as IDictionary<string, object>; 
    Type type = typeof(T); 
    var func = Expression.Lambda<Func<T>>(Expression.MemberInit(
     Expression.New(type), 
     from pair in bindings 
     let member = type.GetMember(pair.Key).Single() 
     select (MemberBinding)Expression.Bind(member, Expression.Constant(pair.Value)))); 
    return func.Compile().Invoke(); 
} 

अंत में, आप एक (सदस्य नाम के खिलाफ keyed) पूर्व संकलित Action<object> setters के सेट कैश हो सकता है। हकीकत में यह शायद आपकी सबसे अच्छी शर्त है। गुण आसान हैं (आप Delegate.CreateDelegate का उपयोग करते हैं) - फ़ील्ड को डायनामिक मोड की आवश्यकता हो सकती है - लेकिन यदि आप पहले से लेआउट की भविष्यवाणी नहीं कर सकते हैं तो इसमें कम से कम ओवरहेड होगा।

बंद/आईएल दृष्टिकोण के लिए

(आप तेजी से नहीं मिलेगा):

public class dynamic_data_serializer<T> 
{ 
    public T create(object obj) 
    { 
     T inst = Activator.CreateInstance<T>(); 
     var bindings = obj as IDictionary<string, object>; 
     foreach (var pair in bindings) 
     { 
      setters[pair.Key](inst, pair.Value); 
     } 
     return inst; 
    } 
    private static readonly Dictionary<string, Action<T, object>> setters; 
    static dynamic_data_serializer() 
    { 
     setters = new Dictionary<string, Action<T, object>>(StringComparer.Ordinal); 
     foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) { 
      setters.Add(prop.Name, CreateForMember(prop)); 
     } 
     foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)) { 
      setters.Add(field.Name, CreateForMember(field)); 
     } 
    } 
    static Action<T, object> CreateForMember(MemberInfo member) 
    { 
     bool isField; 
     Type type; 
     switch (member.MemberType) { 
      case MemberTypes.Property: 
       isField = false; 
       type = ((PropertyInfo)member).PropertyType; 
       break; 
      case MemberTypes.Field: 
       isField = true; 
       type = ((FieldInfo)member).FieldType; 
       break; 
      default: 
       throw new NotSupportedException(); 
     } 
     DynamicMethod method = new DynamicMethod("__set_" + member.Name, null, new Type[] { typeof(T), typeof(object) }); 
     ILGenerator il = method.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     if(type != typeof(object)) { 
      il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type); 
     } 
     if (isField) {il.Emit(OpCodes.Stfld, (FieldInfo)member);} 
     else { il.EmitCall(OpCodes.Callvirt, ((PropertyInfo)member).GetSetMethod(), null); } 

     il.Emit(OpCodes.Ret); 
     return (Action<T, object>)method.CreateDelegate(typeof(Action<T, object>)); 
    } 
} 
+0

आपका अंतिम अनुच्छेद मैंने जो पोस्ट किया है, उतना ही लगता है। – Gabe

+0

@gabe - क्योंकि कुछ कुंजी वाले सेटर्स धारण करना एक उचित दृष्टिकोण है। 'DynamicMethod' दिखाने के लिए अपडेट किया गया है, इसलिए यह फ़ील्ड के साथ काम करेगा - खुश? –

+0

बहुत अच्छा! सेटर्स उत्पन्न करने के लिए आईएल उत्सर्जित करने का यह एक अच्छा उदाहरण है। – Gabe

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