【问题标题】:Dynamically convert a list of properties into an object将属性列表动态转换为对象
【发布时间】:2013-03-01 20:04:12
【问题描述】:

我正在设计一种在 C# MVC(4) 应用程序中声明路由的自动化方式的原型,因此我决定为控制器的方法使用自定义属性:

[RouteUrl("foo/{param}")]
[RouteConstraint("param", "[a-z]+[0-9]+")]
public ActionResult MyAction(string param)
{
    return View();
}

自定义属性允许我单独收集信息,然后我可以将这对参数/约束存储在字典中。

问题是在 RouteCollection.MapRoute() 内部调用的 new Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, ...) 仅将 RouteValueDictionary(IDictionary<string,object>)RouteValueDictionary(object) 作为参数作为默认值和约束。

所以我不知道如何将一对值转换为一个对象(或转换为 RouteValueDictionary)。

是否可以将 Dictionary 转换为 PHP 中的对象?

(object)array("prop1" => "value1", "prop2" => "value2", ...)

也许我应该采取不同的方法,知道吗?

【问题讨论】:

  • 请不要重新发明轮子,而是看看AttributeRouting project
  • 处理后你实际上如何存储param
  • 我使用反射来选择具有自定义属性的控制器方法,然后将参数名称与属性值存储在字典中。

标签: c# asp.net-mvc custom-attributes


【解决方案1】:

嗯,我刚刚意识到Dictionary<string, object>Dictionary<string, string> 兼容,所以我可以为所欲为。

【讨论】:

    【解决方案2】:

    具有 C# 4.0 的一些动态特性

    Dictionary<string, object> dic = new Dictionary<string,object>() {
    
        { "LastName", "Doe"  },
        { "FirstName", "Joe" },
        { "Age", 35 }
    };
    
    dynamic o = new System.Dynamic.ExpandoObject();
    
    foreach(var e in dic)
    {
        var oo = o as IDictionary<String, object>;
        oo[e.Key] = e.Value;
    }
    
    foreach(var a in o)
    {
        Console.WriteLine("{0}={1}", a.Key, (o as IDictionary<String, object>)[a.Key]);
    }
    

    另请查看 github 上的 DynamicSugar.net

    【讨论】:

      【解决方案3】:

      你在找这个吗?

      (object) new { prop1 = "value1", prop2 = "value2", ...}
      

      【讨论】:

      • 我不这么认为 - 我认为 OP 希望名称(prop1prop2)也可以根据输入进行更改。
      • 不,它和new {...} 一样 :) 更多类似 php 的东西:$obj = (object)null; foreach ($propertiesMap as $key =&gt; $value){ $obj-&gt;key = $value }
      【解决方案4】:

      类似的东西))

      public abstract class DynamicClass
          {
              public override string ToString()
              {
                  PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
                  StringBuilder sb = new StringBuilder();
                  sb.Append("{");
                  for (int i = 0; i < props.Length; i++)
                  {
                      if (i > 0) sb.Append(", ");
                      sb.Append(props[i].Name);
                      sb.Append("=");
                      sb.Append(props[i].GetValue(this, null));
                  }
                  sb.Append("}");
                  return sb.ToString();
              }
          }
      
          //DynamicTypeFactory
          public static class ExpressionHelper //simplified
          {
      
              private const string DYNAMIC_ASSEMBLY_NAME = "DynamicAssembly";
              private const string DYNAMIC_MODULE_NAME = "DynamicModule";
              private const string DYNAMIC_CLASS_PREFIX = "DynamicClass";
              private static ModuleBuilder _moduleBuilder;
      
              [SecurityCritical]
              static ExpressionHelper()
              {
                  //var assemblyName = new AssemblyName(DYNAMIC_ASSEMBLY_NAME);
      
                  AssemblyBuilder assembly = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName(DYNAMIC_ASSEMBLY_NAME), AssemblyBuilderAccess.Run);
                  assembly.GetName().SetPublicKey(Assembly.GetExecutingAssembly().GetName().GetPublicKey());
                  assembly.GetName().SetPublicKeyToken(Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken());
                  _moduleBuilder = assembly.DefineDynamicModule(DYNAMIC_MODULE_NAME, true);
      
              }
      
              public static Type CreateType(IDictionary<string, Type> propertyTypes)
              {
                  var typeName = DYNAMIC_CLASS_PREFIX + propertyTypes.GetHashCode().ToString();
                  TypeBuilder typeBuilder = _moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public, typeof(DynamicClass));
                  FieldInfo[] fields = GenerateProperties(typeBuilder, propertyTypes);
                  GenerateEquals(typeBuilder, fields);
                  GenerateGetHashCode(typeBuilder, fields);
                  Type result = typeBuilder.CreateType();
                  return result;
              }
      
              public static object CreateObject(IDictionary<string, object> propertyValues)
              {
                  var propertyTypes = propertyValues.ToDictionary(pair => pair.Key, pair => pair.Value == null ? typeof(object) : pair.Value.GetType());
                  var type = CreateType(propertyTypes);
      
                  Expression targetExpression = Expression.New(type.GetConstructors()[0]); 
      
                  var lambda = Expression.Lambda(targetExpression);
                  var target = lambda.Compile().DynamicInvoke();
      
                  List<MemberBinding> bindings = new List<MemberBinding>();
      
                  foreach (var pair in propertyValues)
                  {
                      bindings.Add(Expression.Bind(type.GetProperty(pair.Key), Expression.Constant(pair.Value)));
                  }
                  return Expression.Lambda(Expression.MemberInit(Expression.New(type), bindings.ToArray())).Compile().DynamicInvoke();
              }
      
              private static FieldInfo[] GenerateProperties(TypeBuilder typeBuilder, IDictionary<string, Type> propertyTypes)
              {
                  FieldInfo[] fields = new FieldBuilder[propertyTypes.Count];
                  for (int i = 0; i < propertyTypes.Count; i++)
                  {
                      var dp = propertyTypes.ElementAt(i);
      
                      var fb = typeBuilder.DefineField("<" + dp.Key + ">k__BackingField", dp.Value, FieldAttributes.Private); //HasDefault?
                      var pb = typeBuilder.DefineProperty(dp.Key, PropertyAttributes.HasDefault, dp.Value, null);
      
      
                      var mbGet = typeBuilder.DefineMethod("get_" + dp.Key,
                          MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, 
                          dp.Value, Type.EmptyTypes);
      
                      var getterGenerator = mbGet.GetILGenerator();
                      getterGenerator.Emit(OpCodes.Ldarg_0);
                      getterGenerator.Emit(OpCodes.Ldfld, fb);
                      getterGenerator.Emit(OpCodes.Ret);
                      //setterBuilder
                      MethodBuilder mbSet = typeBuilder.DefineMethod("set_" + dp.Key,
                          MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, 
                          null, new Type[] { dp.Value });
                      //setterILGenerator
                      var setterGenerator = mbSet.GetILGenerator();
                      setterGenerator.Emit(OpCodes.Ldarg_0);
                      setterGenerator.Emit(OpCodes.Ldarg_1);
                      setterGenerator.Emit(OpCodes.Stfld, fb);
                      setterGenerator.Emit(OpCodes.Ret);
                      pb.SetGetMethod(mbGet);
                      pb.SetSetMethod(mbSet);
                      fields[i] = fb;
                  }
                  return fields;
              }
      
              private static void GenerateEquals(TypeBuilder typeBuilder, FieldInfo[] fields)
              {
                  var mb = typeBuilder.DefineMethod("Equals",
                      MethodAttributes.Public | MethodAttributes.ReuseSlot |
                      MethodAttributes.Virtual | MethodAttributes.HideBySig,
                      typeof(bool), new Type[] { typeof(object) });
                  var generator = mb.GetILGenerator();
                  var other = generator.DeclareLocal(typeBuilder);
                  var next = generator.DefineLabel();
                  generator.Emit(OpCodes.Ldarg_1);
                  generator.Emit(OpCodes.Isinst, typeBuilder);
                  generator.Emit(OpCodes.Stloc, other);
                  generator.Emit(OpCodes.Ldloc, other);
                  generator.Emit(OpCodes.Brtrue_S, next);
                  generator.Emit(OpCodes.Ldc_I4_0);
                  generator.Emit(OpCodes.Ret);
                  generator.MarkLabel(next);
                  foreach (var field in fields)
                  {
                      var ft = field.FieldType;
                      var ct = typeof(EqualityComparer<>).MakeGenericType(ft);
                      next = generator.DefineLabel();
                      generator.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null);
                      generator.Emit(OpCodes.Ldarg_0);
                      generator.Emit(OpCodes.Ldfld, field);
                      generator.Emit(OpCodes.Ldloc, other);
                      generator.Emit(OpCodes.Ldfld, field);
                      generator.EmitCall(OpCodes.Callvirt, ct.GetMethod("Equals", new Type[] { ft, ft }), null);
                      generator.Emit(OpCodes.Brtrue_S, next);
                      generator.Emit(OpCodes.Ldc_I4_0);
                      generator.Emit(OpCodes.Ret);
                      generator.MarkLabel(next);
                  }
                  generator.Emit(OpCodes.Ldc_I4_1);
                  generator.Emit(OpCodes.Ret);
              }
      
              private static void GenerateGetHashCode(TypeBuilder typeBuilder, FieldInfo[] fields)
              {
                  var mb = typeBuilder.DefineMethod("GetHashCode",
                      MethodAttributes.Public | MethodAttributes.ReuseSlot |
                      MethodAttributes.Virtual | MethodAttributes.HideBySig,
                      typeof(int), Type.EmptyTypes);
                  var generator = mb.GetILGenerator();
                  generator.Emit(OpCodes.Ldc_I4_0);
                  foreach (FieldInfo field in fields)
                  {
                      var ft = field.FieldType;
                      var ct = typeof(EqualityComparer<>).MakeGenericType(ft);
                      generator.EmitCall(OpCodes.Call, ct.GetMethod("get_Default"), null);
                      generator.Emit(OpCodes.Ldarg_0);
                      generator.Emit(OpCodes.Ldfld, field);
                      generator.EmitCall(OpCodes.Callvirt, ct.GetMethod("GetHashCode", new Type[] { ft }), null);
                      generator.Emit(OpCodes.Xor);
                  }
                  generator.Emit(OpCodes.Ret);
              }
          }
      

      使用:

      var obj = ExpressionHelper.CreateObject(new Dictionary<string, object>()
                                                  {
                                                      {"testInt32Property", int.MaxValue},
                                                      {"testStringProperty", "TestString"}
                                                  });
      

      【讨论】:

        猜你喜欢
        • 2021-11-02
        • 1970-01-01
        • 1970-01-01
        • 2018-10-14
        • 1970-01-01
        • 1970-01-01
        • 2017-07-07
        • 2019-12-16
        • 1970-01-01
        相关资源
        最近更新 更多