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

使用C中的反射从字符串获取属性值#

  •  756
  • pedrofernandes  · 技术社区  · 15 年前

    我正在尝试实现 Data transformation using Reflection 我的代码中的示例。

    这个 GetSourceValue 函数有一个比较各种类型的开关,但我想删除这些类型和属性,并 获取源程序 仅使用单个字符串作为参数获取属性的值。我想在字符串中传递一个类和属性,并解析该属性的值。

    这有可能吗?

    Web Archive version of original blog post

    21 回复  |  直到 5 年前
        1
  •  1492
  •   Ed Swangren    11 年前
     public static object GetPropValue(object src, string propName)
     {
         return src.GetType().GetProperty(propName).GetValue(src, null);
     }
    

    当然,您需要添加验证和其他内容,但这就是其中的要点。

        2
  •  188
  •   jheddings    12 年前

    像这样的怎么样:

    public static Object GetPropValue(this Object obj, String name) {
        foreach (String part in name.Split('.')) {
            if (obj == null) { return null; }
    
            Type type = obj.GetType();
            PropertyInfo info = type.GetProperty(part);
            if (info == null) { return null; }
    
            obj = info.GetValue(obj, null);
        }
        return obj;
    }
    
    public static T GetPropValue<T>(this Object obj, String name) {
        Object retval = GetPropValue(obj, name);
        if (retval == null) { return default(T); }
    
        // throws InvalidCastException if types are incompatible
        return (T) retval;
    }
    

    这将允许您使用单个字符串下降到属性中,如下所示:

    DateTime now = DateTime.Now;
    int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
    int hrs = now.GetPropValue<int>("TimeOfDay.Hours");
    

    您可以将这些方法用作静态方法或扩展。

        3
  •  52
  •   Eduardo Cuomo Sajjad Ali Khan    10 年前

    添加到任何 Class :

    public class Foo
    {
        public object this[string propertyName]
        {
            get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
            set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
        }
    
        public string Bar { get; set; }
    }
    

    然后,您可以将其用作:

    Foo f = new Foo();
    // Set
    f["Bar"] = "asdf";
    // Get
    string s = (string)f["Bar"];
    
        4
  •  41
  •   J0e3gan    10 年前

    用这个怎么样 CallByName Microsoft.VisualBasic 命名空间(命名空间) Microsoft.VisualBasic.dll )?它使用反射来获取普通对象、COM对象甚至动态对象的属性、字段和方法。

    using Microsoft.VisualBasic;
    using Microsoft.VisualBasic.CompilerServices;
    

    然后

    Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
    
        5
  •  26
  •   Devsined    10 年前

    杰丁斯的回答很好。我希望改进它,允许引用聚合数组或对象集合,以便propertyname可以是property1.property2[x].property3:

        public static object GetPropertyValue(object srcobj, string propertyName)
        {
            if (srcobj == null)
                return null;
    
            object obj = srcobj;
    
            // Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
            string[] propertyNameParts = propertyName.Split('.');
    
            foreach (string propertyNamePart in propertyNameParts)
            {
                if (obj == null)    return null;
    
                // propertyNamePart could contain reference to specific 
                // element (by index) inside a collection
                if (!propertyNamePart.Contains("["))
                {
                    PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart);
                    if (pi == null) return null;
                    obj = pi.GetValue(obj, null);
                }
                else
                {   // propertyNamePart is areference to specific element 
                    // (by index) inside a collection
                    // like AggregatedCollection[123]
                    //   get collection name and element index
                    int indexStart = propertyNamePart.IndexOf("[")+1;
                    string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1);
                    int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1));
                    //   get collection object
                    PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName);
                    if (pi == null) return null;
                    object unknownCollection = pi.GetValue(obj, null);
                    //   try to process the collection as array
                    if (unknownCollection.GetType().IsArray)
                    {
                        object[] collectionAsArray = unknownCollection as Array[];
                        obj = collectionAsArray[collectionElementIndex];
                    }
                    else
                    {
                        //   try to process the collection as IList
                        System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList;
                        if (collectionAsList != null)
                        {
                            obj = collectionAsList[collectionElementIndex];
                        }
                        else
                        {
                            // ??? Unsupported collection type
                        }
                    }
                }
            }
    
            return obj;
        }
    
        6
  •  11
  •   testing    6 年前

    如果我使用的代码来自 Ed S. 我得到

    “ReflectionExtensions.GetProperty(Type,String)”由于其保护级别而不可访问

    似乎 GetProperty() 在xamarin.forms中不可用。 TargetFrameworkProfile Profile7 在我的可移植类库(.NET Framework 4.5、Windows 8、ASP.NET Core 1.0、Xamarin.android、Xamarin.ios、Xamarin.ios classic)中。

    现在我找到了一个可行的解决方案:

    using System.Linq;
    using System.Reflection;
    
    public static object GetPropValue(object source, string propertyName)
    {
        var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
        return property?.GetValue(source);
    }
    

    Source

        7
  •  8
  •   Rubens Farias    9 年前

    关于嵌套属性讨论,如果使用 DataBinder.Eval Method (Object, String) 如下:

    var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours");
    

    当然,您需要在 System.Web 装配,但这可能不是什么大问题。

        8
  •  6
  •   Matt Frear    7 年前

    .NET标准中要调用的方法已更改(从1.6开始)。我们还可以使用C 6的空条件运算符。

    using System.Reflection; 
    public static object GetPropValue(object src, string propName)
    {
        return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
    }
    
        9
  •  4
  •   tom redfern    9 年前

    使用的属性信息 系统反射 命名空间。无论我们尝试访问什么属性,反射编译都很好。运行时出错。

        public static object GetObjProperty(object obj, string property)
        {
            Type t = obj.GetType();
            PropertyInfo p = t.GetProperty("Location");
            Point location = (Point)p.GetValue(obj, null);
            return location;
        }
    

    它可以很好地获取对象的位置属性

    Label1.Text = GetObjProperty(button1, "Location").ToString();
    

    我们会得到位置:x=71,y=27 我们也可以用同样的方法返回location.x或location.y。

        10
  •  4
  •   Boncho Valkov    9 年前
    public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
        {
            var result = new List<KeyValuePair<string, string>>();
            if (item != null)
            {
                var type = item.GetType();
                var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (var pi in properties)
                {
                    var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
                    if (selfValue != null)
                    {
                        result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
                    }
                    else
                    {
                        result.Add(new KeyValuePair<string, string>(pi.Name, null));
                    }
                }
            }
            return result;
        }
    

    这是一种在列表中获取所有属性及其值的方法。

        11
  •  2
  •   Recursor    10 年前

    这是另一种查找嵌套属性的方法,不需要字符串来告诉您嵌套路径。对于单属性方法,请归功于Ed S.。

        public static T FindNestedPropertyValue<T, N>(N model, string propName) {
            T retVal = default(T);
            bool found = false;
    
            PropertyInfo[] properties = typeof(N).GetProperties();
    
            foreach (PropertyInfo property in properties) {
                var currentProperty = property.GetValue(model, null);
    
                if (!found) {
                    try {
                        retVal = GetPropValue<T>(currentProperty, propName);
                        found = true;
                    } catch { }
                }
            }
    
            if (!found) {
                throw new Exception("Unable to find property: " + propName);
            }
    
            return retVal;
        }
    
            public static T GetPropValue<T>(object srcObject, string propName) {
            return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null);
        }
    
        12
  •  2
  •   Irshad Kredns    9 年前

    你从来没有提到你要检查的对象,而且由于你拒绝了引用一个给定对象的对象,我假设你是指一个静态对象。

    using System.Reflection;
    public object GetPropValue(string prop)
    {
        int splitPoint = prop.LastIndexOf('.');
        Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint));
        object obj = null;
        return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null);
    }
    

    注意,我用局部变量标记了正在检查的对象 obj . null 表示静态,否则将其设置为您想要的。还要注意, GetEntryAssembly() 是获取“正在运行”程序集的几种可用方法之一,如果在加载类型时遇到困难,您可能会希望使用它。

        13
  •  2
  •   Irshad Kredns    9 年前

    下面的代码是一个递归方法,用于显示对象实例中包含的所有属性名和值的整个层次结构。此方法使用了Alexd的简化版本 GetPropertyValue() 回答上面的这个问题。多亏了这条讨论线,我才知道该怎么做!

    例如,我使用此方法显示 WebService 通过调用以下方法进行响应:

    PropertyValues_byRecursion("Response", response, false);

    public static object GetPropertyValue(object srcObj, string propertyName)
    {
      if (srcObj == null) 
      {
        return null; 
      }
      PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", ""));
      if (pi == null)
      {
        return null;
      }
      return pi.GetValue(srcObj);
    }
    
    public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues)
    {
      /// Processes all of the objects contained in the parent object.
      ///   If an object has a Property Value, then the value is written to the Console
      ///   Else if the object is a container, then this method is called recursively
      ///       using the current path and current object as parameters
    
      // Note:  If you do not want to see null values, set showNullValues = false
    
      foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties())
      {
        // Build the current object property's namespace path.  
        // Recursion extends this to be the property's full namespace path.
        string currentPath = parentPath + "." + pi.Name;
    
        // Get the selected property's value as an object
        object myPropertyValue = GetPropertyValue(parentObj, pi.Name);
        if (myPropertyValue == null)
        {
          // Instance of Property does not exist
          if (showNullValues)
          {
            Console.WriteLine(currentPath + " = null");
            // Note: If you are replacing these Console.Write... methods callback methods,
            //       consider passing DBNull.Value instead of null in any method object parameters.
          }
        }
        else if (myPropertyValue.GetType().IsArray)
        {
          // myPropertyValue is an object instance of an Array of business objects.
          // Initialize an array index variable so we can show NamespacePath[idx] in the results.
          int idx = 0;
          foreach (object business in (Array)myPropertyValue)
          {
            if (business == null)
            {
              // Instance of Property does not exist
              // Not sure if this is possible in this context.
              if (showNullValues)
              {
                Console.WriteLine(currentPath  + "[" + idx.ToString() + "]" + " = null");
              }
            }
            else if (business.GetType().IsArray)
            {
              // myPropertyValue[idx] is another Array!
              // Let recursion process it.
              PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
            }
            else if (business.GetType().IsSealed)
            {
              // Display the Full Property Path and its Value
              Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString());
            }
            else
            {
              // Unsealed Type Properties can contain child objects.
              // Recurse into my property value object to process its properties and child objects.
              PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
            }
            idx++;
          }
        }
        else if (myPropertyValue.GetType().IsSealed)
        {
          // myPropertyValue is a simple value
          Console.WriteLine(currentPath + " = " + myPropertyValue.ToString());
        }
        else
        {
          // Unsealed Type Properties can contain child objects.
          // Recurse into my property value object to process its properties and child objects.
          PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues);
        }
      }
    }
    
        14
  •  2
  •   Rahma Samaroon    7 年前
    public static TValue GetFieldValue<TValue>(this object instance, string name)
    {
        var type = instance.GetType(); 
        var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name);
        return (TValue)field?.GetValue(instance);
    }
    
    public static TValue GetPropertyValue<TValue>(this object instance, string name)
    {
        var type = instance.GetType();
        var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name);
        return (TValue)field?.GetValue(instance);
    }
    
        15
  •  2
  •   Komal Narang    6 年前
    public class YourClass
    {
        //Add below line in your class
        public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null);
        public string SampleProperty { get; set; }
    }
    
    //And you can get value of any property like this.
    var value = YourClass["SampleProperty"];
    
        16
  •  1
  •   Glory Raj    13 年前
    Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
    
        17
  •  1
  •   andrewsi Laxman Battini    10 年前

    更短的路……

    var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now};
    var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now };
    
    var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())==
                  string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray());
    
        18
  •  1
  •   Community kavare    7 年前

    jheddings AlexD 他们都写了关于如何解析属性字符串的很好的答案。我想把我的混合,因为我写了一个专门的图书馆正是为了这个目的。

    Pather.CSharp 的主要类是 Resolver . 默认情况下,它可以解析属性、数组和字典条目。

    例如,如果有这样的对象

    var o = new { Property1 = new { Property2 = "value" } };
    

    想要得到 Property2 ,您可以这样做:

    IResolver resolver = new Resolver();
    var path = "Property1.Property2";
    object result = r.Resolve(o, path); 
    //=> "value"
    

    这是最基本的路径示例。如果你想看看它还能做什么,或者你能怎么延伸它,就去看看它。 Github page .

        19
  •  0
  •   user3175253    6 年前

    这是我的解决方案。它还与COM对象一起工作,并允许从COM对象访问集合/数组项。

    public static object GetPropValue(this object obj, string name)
    {
        foreach (string part in name.Split('.'))
        {
            if (obj == null) { return null; }
    
            Type type = obj.GetType();
            if (type.Name == "__ComObject")
            {
                if (part.Contains('['))
                {
                    string partWithoundIndex = part;
                    int index = ParseIndexFromPropertyName(ref partWithoundIndex);
                    obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
                }
                else
                {
                    obj = Versioned.CallByName(obj, part, CallType.Get);
                }
            }
            else
            {
                PropertyInfo info = type.GetProperty(part);
                if (info == null) { return null; }
                obj = info.GetValue(obj, null);
            }
        }
        return obj;
    }
    
    private static int ParseIndexFromPropertyName(ref string name)
    {
        int index = -1;
        int s = name.IndexOf('[') + 1;
        int e = name.IndexOf(']');
        if (e < s)
        {
            throw new ArgumentException();
        }
        string tmp = name.Substring(s, e - s);
        index = Convert.ToInt32(tmp);
        name = name.Substring(0, s - 1);
        return index;
    }
    
        20
  •  0
  •   Hennadii    6 年前

    看看 Heleonix.Reflection 图书馆。您可以通过路径获取/设置/调用成员,或者创建一个比反射更快的getter/setter(编译为委托的lambda)。例如:

    var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);
    

    或者创建一次getter并缓存以供重用(这更具性能,但如果中间成员为空,则可能引发NullReferenceException):

    var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
    getter(DateTime.Now);
    

    或者如果你想创建一个 List<Action<object, object>> 对于不同的getter,只需为已编译的委托指定基类型(类型转换将添加到已编译的lambda中):

    var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
    getter(DateTime.Now);
    
        21
  •  0
  •   OneCricketeer Gabriele Mariotti    5 年前

    以下方法非常适合我:

    class MyClass {
        public string prop1 { set; get; }
    
        public object this[string propertyName]
        {
            get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
            set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
        }
    }
    

    要获取属性值:

    MyClass t1 = new MyClass();
    ...
    string value = t1["prop1"].ToString();
    

    要设置属性值:

    t1["prop1"] = value;