将属性列表动态转换为对象

时间:2013-03-01 20:04:13

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

我正在原型化一种在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", ...)

也许我应该采取不同的方法,任何想法?

4 个答案:

答案 0 :(得分:1)

嗯,我刚刚意识到Dictionary<string, object>Dictionary<string, string>兼容,所以我能够做我想做的一切。

答案 1 :(得分:1)

使用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

答案 2 :(得分:0)

你在找这个吗?

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

答案 3 :(得分:0)

像这样的东西))

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