2010-03-01 28 views
8

Tôi đang tìm cách tổng quát hơn/"chuẩn" để khởi tạo đối tượng của một kiểu T từ tập hợp các cặp chuỗi, đối tượng. Đối với tôi có vẻ như có một số cách nổi tiếng để làm điều đó nhưng tôi không thể tìm thấy nó, vì vậy tôi đưa ra đoạn mã này. Có ai biết điều gì tốt hơn không? Khởi tạo đối tượng kiểu T từ điển <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 }); 
     } 
    } 
} 

Trả lời

2

DataContractJsonSerializer quá chậm, nhưng bạn đang sử dụng sự phản chiếu? Nếu bạn phải deserialize rất nhiều đối tượng, tôi sẽ khuyên bạn nên sử dụng lambdas biên dịch thay vì phản ánh. Một lambda chỉ có thể thiết lập các thuộc tính, không phải các trường (ít nhất là trong .Net 3.5), vì vậy bạn có thể phải điều chỉnh các lớp mà bạn sử dụng nó, nhưng nó đáng giá vì nó nhanh gấp 1000 lần.

Dưới đây là một chức năng mà tạo ra một setter tài sản được đưa ra một loại và một PropertyInfo cho tài sản để thiết lập:

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

Bạn sẽ có một cuốn từ điển của setters cho mỗi lớp, và bất cứ khi nào bạn cần phải thiết lập một tài sản của một lớp, bạn sẽ tra cứu setter cho thuộc tính đó trong từ điển và gọi nó với giá trị để gán, như sau: setters[propName](_this_, value);

+0

Cảm ơn bạn! Tôi sẽ cố gắng sử dụng nó. Trong trường hợp sử dụng sự phản chiếu, tất cả các thông tin kiểu có thể được lưu trữ. –

0

DataContractJsonSerializer

Tại sao bạn sẽ làm cho một serializer tùy chỉnh và không sử dụng DataContractJsonSerializer?

EDIT

Nếu DataContractJsonSerializer không phù hợp với bạn, bạn có thể thử JSON.Net. Thực hiện một serializer hiệu quả không phải là một nhiệm vụ dễ dàng, có rất nhiều cạm bẫy và trường hợp đặc biệt mà bạn có thể không muốn nhận được vào. Bằng cách này, mẫu mã của bạn sử dụng nhiều sự phản chiếu chậm, tôi nghi ngờ rằng nó sẽ hoạt động tốt hơn DataContractJsonSerializer hoặc JSON.Net.

+0

DataContractJsonSerializer là chậm nói chung và sử dụng nó trong trường hợp của tôi gây ra vấn đề hiệu suất. –

+0

Cảm ơn Jeff. Tôi sẽ xem xét JSON.Net. Tôi đồng ý, phản ánh cũng chậm, nhưng tôi có thể lưu trữ tất cả thông tin loại. –

+0

Ngay cả khi bạn nhập thông tin về loại bộ nhớ cache, sử dụng Activator.CreateInstance và FieldInfo.SetValue vẫn còn chậm. Để thực sự tăng tốc độ, bạn sẽ phải sử dụng những thứ như Expression.Compile, Open delegates hoặc reflection.emit. Điều quan trọng là sử dụng sự phản chiếu chỉ một lần cho mỗi loại. –

1

Tôi có thể đề xuất FormatterServices.PopulateObjectMembers, ngoại trừ: đây vẫn là AFAIK chậm và b: Tôi đã thử nó (bên dưới) và có vẻ như muốn ném một ngoại lệ vào tài sản (don không biết tại sao; không nhìn quá sâu). Một tùy chọn khác có thể là Expression, nhưng bạn không thực sự muốn thực hiện Compile mỗi lần (tốt hơn để làm điều đó một lần duy nhất và lưu vào bộ nhớ cache, nhưng yêu cầu định dạng đã biết).

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

Cuối cùng, bạn có thể bộ nhớ cache một bộ pre-biên soạn Action<object> setters (keyed chống lại tên thành viên). Trong thực tế, đây có lẽ là đặt cược tốt nhất của bạn. Các thuộc tính rất dễ (bạn sử dụng Delegate.CreateDelegate) - các trường có thể cần DynamicMethod - nhưng nếu bạn không thể dự đoán trước được bố cục thì nó sẽ có ít chi phí.

Đối với phương pháp tiếp cận có khóa/IL (bạn sẽ không nhận được nhanh hơn):

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

Đoạn cuối cùng của bạn nghe rất giống với những gì tôi đã đăng. – Gabe

+0

@gabe - bởi vì việc giữ một vài người định cư có khóa là một cách tiếp cận hợp lý. Cập nhật để hiển thị 'DynamicMethod' do đó nó sẽ làm việc với các trường - hạnh phúc hơn? –

+0

Rất đẹp! Đó là một ví dụ tốt về việc phát ra IL để tạo ra những người định cư. – Gabe

Các vấn đề liên quan