代码之家  ›  专栏  ›  技术社区  ›  Oxymoron

从属性的表示中获取属性

  •  0
  • Oxymoron  · 技术社区  · 15 年前

    我对这个头衔有些怀疑,但我想不出更好的了。

    假设我有以下枚举

    public enum ClassProperties
    {
         Primary = 0,
         Secondary = 1,
    }
    

    一个看起来像这样的类

    public class Test
    {
        Primary { get { return _primary; }}
        Secondary { get { return _secondary; }}
        // more irrelevant properties
    }
    

    foreach(ClassProperties myProp = Enum.GetValues(typeof(ClassProperties)))
    {
        Test t = new Test();
        t.myProp // <- this is what I'm after
        // so if myProp equals Primary,
        // t.Primary is called...
    }
    

    这会让你知道我想做什么,但尝试它会让我觉得自己像一个刚刚弄湿自己的流浪汉。只是感觉不对。

    4 回复  |  直到 15 年前
        1
  •  2
  •   Ian    15 年前

    您可以使用反射来检索属性。然后,将根据属性的名称定位该属性。

    Test t = new Test();
    Type testType = t.GetType();
    PropertyInfo[] properties = testType.GetProperties();
    

    欲了解更多信息,请参阅 GetProperties() method &返回 PropertyInfo 类型。

        2
  •  2
  •   Thomas Levesque    15 年前
    foreach(ClassProperties myProp in Enum.GetValues(typeof(ClassProperties)))
    {
        Test t = new Test();
        PropertyInfo prop = typeof(Test).GetProperty(myProp.ToString());
        // Get
        object value = prop.GetValue(t, null);
        // Set
        prop.SetValue(t, newValue, null);
    }
    
        3
  •  0
  •   Josh Barker    15 年前

        4
  •  0
  •   devilmaster    15 年前

    至少有两种方法可以做到这一点:

    1.反射和属性信息

    var obj = new TestClass();
    var allProps = typeof(TestClass).GetProperties();
    foreach (var prop in allProps)
    {
        // Get propertie value
        object propValue = prop.GetGetMethod().Invoke(obj, null);
        //Set propertie value
        prop.GetSetMethod().Invoke(obj, new object[] { propValue });
    }
    

    不过,在性能方面,您应该小心,对于一个设置了两个属性的类,进行一次粗略的测试,并获得所有属性10k次,反射需要0.06秒,手工编写则需要0.001秒。因此,对性能的打击相当激烈

    2.动态方法 这种方法比较复杂,但性能非常值得。动态方法是程序在运行时为其发出MSIL的方法。它们由运行时执行,就像它们是由编译器创建的一样(因此速度非常好)。使用此方法设置并获取类10k上的2个属性花费了0.004秒(相比之下,反射和手动分别为0.06秒和0.001秒)。下面是为特定类型的getter和setter生成委托数组的代码。生成动态代理的成本可能很高,因此如果您打算多次使用代理(您可能会这样做),则应该缓存代理。

    //Container for getters and setters of a property
    public class MyProp
    {
        public string PropName { get; set; }
        public Func<object,object> Getter{get;set;}
        public Action<object,object> Setter{get;set;}
    }
    
    public static MyProp[] CreatePropertyDelagates (Type type)
    {
          var allProps = type.GetProperties();
          var props = new MyProp[allProps.Length];
    
          for(int i =0;i<allProps.Length;i++)
          {
                var prop = allProps[i];
                // Getter dynamic method the signature would be :
                // object Get(object thisReference)
                // { return ((TestClass)thisReference).Prop; }
    
                DynamicMethod dmGet = new DynamicMethod("Get", typeof(object), new Type[] { typeof(object), });
                ILGenerator ilGet = dmGet.GetILGenerator();
                // Load first argument to the stack
                ilGet.Emit(OpCodes.Ldarg_0);
                // Cast the object on the stack to the apropriate type
                ilGet.Emit(OpCodes.Castclass, type);
                // Call the getter method passing the object on the stack as the this reference
                ilGet.Emit(OpCodes.Callvirt, prop.GetGetMethod());
                // If the property type is a value type (int/DateTime/..) box the value so we can return it
                if (prop.PropertyType.IsValueType)
                {
                      ilGet.Emit(OpCodes.Box, prop.PropertyType);
                }
                // Return from the method
                ilGet.Emit(OpCodes.Ret);
    
    
                // Setter dynamic method the signature would be :
                // object Set(object thisReference, object propValue)
                // { return ((TestClass)thisReference).Prop = (PropType)propValue; }
    
                DynamicMethod dmSet = new DynamicMethod("Set", typeof(void), new Type[] { typeof(object), typeof(object) });
                ILGenerator ilSet = dmSet.GetILGenerator();
                // Load first argument to the stack and cast it
                ilSet.Emit(OpCodes.Ldarg_0);
                ilSet.Emit(OpCodes.Castclass, type);
    
                // Load secons argument to the stack and cast it or unbox it
                ilSet.Emit(OpCodes.Ldarg_1);
                if (prop.PropertyType.IsValueType)
                {
                      ilSet.Emit(OpCodes.Unbox_Any,prop.PropertyType);
                }
                else
                {
                      ilSet.Emit(OpCodes.Castclass, prop.PropertyType);
                }
                // Call Setter method and return 
                ilSet.Emit(OpCodes.Callvirt, prop.GetSetMethod());
                ilSet.Emit(OpCodes.Ret);
    
                // Create the delegates for invoking the dynamic methods and add the to an array for later use
                props[i] =  new MyProp()
                {
                      PropName = prop.Name,
                      Setter = (Action<object, object>)dmSet.CreateDelegate(typeof(Action<object, object>)),
                      Getter = (Func<object, object>)dmGet.CreateDelegate(typeof(Func<object, object>)),
                };
    
          }
          return props;
    }
    

    // Should be cahced for further use 
    var testClassProps = CreatePropertyDelagates(typeof(TestClass));
    
    var obj = new TestClass();
    foreach (var p in testClassProps)
    {
          var propValue = p.Getter(obj);
          p.Setter(obj,propValue);
    }
    

    Obs:上面的代码不处理没有getter或setter或标记为private的属性。这可以通过查看PropertyInfo类的属性并仅在适当的情况下创建委托来轻松完成