2010-03-01 18 views
8

'dan T türünde bir nesneyi ilklendirin Bazı T türlerinin nesnesini çiftler dizesi, nesnesi kümesinden başlatmak için daha genel/"standart" bir yol arıyorum. Benim için bunu yapmak için iyi bilinen bir yol olmalı gibi görünüyor ama bunu bulamıyorum, bu yüzden bu kod ile geliyorum. Daha iyi bir şey bilen var mı? Sözlük <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 }); 
     } 
    } 
} 

cevap

2

DataContractJsonSerializer çok yavaş, ancak yansıma kullanıyorsunuz? Bir çok nesneyi serpmek zorunda kalırsanız, yansıma yerine derlenmiş lambdaları kullanmanızı tavsiye ederim. Bir lambda sadece özellikleri değil, alanları (en azından .Net 3.5) ayarlayabilir, böylece kullandığınız sınıfları ayarlamanız gerekebilir, ancak buna değer çünkü 1000 kat daha hızlıdır. İşte

ayarlamak için bir türünü ve özelliği için bir PropertyInfo verilen bir özellik ayarlayıcı yaratan bir fonksiyon var: Her sınıf için belirleyici Ağustos bir sözlük olurdu

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(); 
    } 

ve bir özelliği ayarlamak zorunda zaman Bir sınıfın sözlüğünde bu özellik için ayarlayıcıyı arar ve aşağıdaki gibi atamak için değer olarak adlandırırsınız: setters[propName](_this_, value);

+0

Teşekkürler! Kullanmaya çalışırdım. Yansıma kullanılması durumunda, tüm tip bilgileri önbelleğe alınabilir. –

0

DataContractJsonSerializer

Neden özel bir seri hale yapıp DataContractJsonSerializer kullanmak ister? DataContractJsonSerializer seni uymuyorsa

DÜZENLEME

, sen JSON.Net deneyebilirsiniz. Bir serileştiriciyi verimli bir şekilde uygulamak kolay bir iş değildir, içine girmek istemeyeceğiniz pek çok tuzak ve özel durum vardır. Bu arada, kod örneğiniz yavaş olan yansıma kullanımını sağlar, DataContractJsonSerializer veya JSON.Net'ten daha iyi performans göstereceğinden şüpheliyim.

+0

DataContractJsonSerializer genelde yavaştır ve benim durumumda bunu kullanarak performans sorunu neden olur. –

+0

Teşekkürler Jeff. JSON.Net'e bakacağım. Katılıyorum, yansıma da yavaş, ama her tür bilgiyi önbelleğe alabilirim. –

+0

Tür bilgileri önbelleğe alsanız bile Activator.CreateInstance ve FieldInfo.SetValue öğelerini kullanmak da yavaştır. Gerçekten işleri hızlandırmak için Expression.Compile, Open delegates veya reflection.emit gibi şeyleri kullanmak zorunda kalacaksınız. Anahtar, yansıma tür başına yalnızca bir kez kullanmaktır. –

1

Aşağıdakiler hariç FormatterServices.PopulateObjectMembers önerebilirim: Bu hala yavaş AFAIK ve b: Ben (aşağıda) denedim ve özellik bir istisna atmak istiyor gibi görünüyor (don nedenini bilmiyorum; çok derin görünmüyordu). Başka bir seçenek Expression olabilir, ancak her seferinde Compile'u gerçekten yapmak istemezsiniz (yalnızca bir kez yapmak ve önbelleğe almak için daha iyi, ancak bilinen bir biçim gerektirir).

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(); 
} 

Son olarak, (üye adı karşı anahtarlı) önceden derlenmiş Action<object> belirleyiciler bir dizi önbelleğe olabilir. Gerçekte bu muhtemelen en iyi bahistir. Özellikler kolaydır (Delegate.CreateDelegate kullanırsınız) - alanlar DynamicMethod'a ihtiyaç duyabilir - ancak düzeni önceden tahmin edemezseniz, en düşük yükü olacaktır. Kama/IL yaklaşım için

(daha hızlı almazsınız):

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

Son paragrafınız, yayınladığımma çok benziyor. – Gabe

+0

@gabe - çünkü birkaç anahtarlı setter tutmak makul bir yaklaşımdır. "DynamicMethod" u gösterecek şekilde güncellendi, böylece alanlarla çalışacak - daha mutlu mu? –

+0

Çok güzel! Bu, setör üretmek için IL yayan iyi bir örnektir. – Gabe

İlgili konular