Obtenga el valor de la propiedad de la cadena usando la reflexión

Estoy intentando implementar el Transformación de datos usando Reflection1 ejemplo en mi código.

El GetSourceValue La función tiene un interruptor que compara varios tipos, pero quiero eliminar estos tipos y propiedades y tener GetSourceValue obtenga el valor de la propiedad utilizando solo una única cadena como parámetro. Quiero pasar una clase y una propiedad en la cadena y resolver el valor de la propiedad.

¿Es esto posible?

1 Versión de archivo web de la publicación de blog original

preguntado el 28 de julio de 09 a las 21:07

22 Respuestas

 public static object GetPropValue(object src, string propName)
 {
     return src.GetType().GetProperty(propName).GetValue(src, null);
 }

Por supuesto, querrá agregar validación y todo eso, pero eso es lo esencial.

contestado el 04 de mayo de 13 a las 03:05

¡Agradable y sencillo! Sin embargo, lo haría genérico: public static T GetPropertyValue<T>(object obj, string propName) { return (T)obj.GetType().GetProperty(propName).GetValue(obj, null); } - Ohad Schneider

Una optimización puede eliminar el riesgo de una excepción nula como esta: "src.GetType().GetProperty(propName)?.GetValue(src, null);";). - shA.t

@ shA.t: Creo que es una mala idea. ¿Cómo se diferencia entre un valor nulo de una propiedad existente o ninguna propiedad? Preferiría saber de inmediato que estoy enviando una propiedad con un nombre incorrecto. Este no es un código de producción, pero una mejor mejora sería lanzar una excepción más específica (por ejemplo, verificar nulo en GetProperty y tirar PropertyNotFoundException o algo si es nulo.) - Ed s.

En caso de que su propiedad sea realmente un Campo y no una Propiedad (como la mía;)) entonces use GetField en lugar de GetProperty - Christopher K.

Qué tal algo como esto:

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

Esto le permitirá descender a las propiedades utilizando una sola cadena, como esta:

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

Puede utilizar estos métodos como métodos estáticos o extensiones.

respondido 08 nov., 12:19

@FredJand ¡me alegro de que te hayas topado con él! Siempre es sorprendente cuando aparecen estos mensajes antiguos. Era un poco vago, así que agregué un poco de texto para explicarlo. También cambié para usarlos como métodos de extensión y agregué un formulario genérico, así que lo agregué aquí. - jheddings

¿Por qué la guardia nula está en el foreach y no arriba? - Santhos

@Santhos dado que 'obj' se redefine en el cuerpo del bucle foreach, se verifica durante cada iteración. - jheddings

Útil, pero en el caso límite de que una de las propiedades anidadas pueda estar oculta (usando el modificador 'nuevo'), arrojará una excepción para encontrar propiedades duplicadas. Sería mejor realizar un seguimiento del último tipo de propiedad y utilizar PropertyInfo.PropertyType en lugar de obj.GetType() en propiedades anidadas, al igual que lo haría acceder a la propiedad en una propiedad anidada. - No

Puede usar el nameof expresión a partir de C # 6 así: nameof(TimeOfDay.Minutes) en el parámetro de nombre al llamar a la función para eliminar cadenas mágicas y agregar seguridad en el tiempo de compilación a estas llamadas. - Recoger

Agregar a cualquier 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; }
}

Entonces, puedes usarlo como:

Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];

Respondido 23 Jul 14, 23:07

@EduardoCuomo: ¿Es posible usar la reflexión con esto para que no necesite saber qué miembros tiene la clase? - Nuestro hombre en bananas

¿Es posible hacer esto si "Bar" fuera un objeto? - agua_grande

@big_water el SetValue y los GetValue métodos funciona con Object. Si necesita trabajar con un tipo específico, debe emitir el resultado de GetValue y lanzar el valor para asignarlo con SetValue - Eduardo Cuomo

Lo siento @OurManinBananas, no puedo entender tu pregunta. ¿Qué es lo que quieres hacer? - Eduardo Cuomo

¿Cuál es el nombre de este tipo de métodos ..? - Sahan Chinthaka

¿Qué hay de usar el CallByName de los Microsoft.VisualBasic espacio de nombresMicrosoft.VisualBasic.dll)? Utiliza la reflexión para obtener propiedades, campos y métodos de objetos normales, objetos COM e incluso objetos dinámicos.

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

y entonces

Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();

respondido 02 nov., 14:21

Sugerencia interesante, una inspección adicional demostró que puede manejar tanto campos como propiedades, objetos COM, e incluso puede manejar correctamente la encuadernación dinámica! - IS4

Recibo un error: No se encontró el miembro público 'MyPropertyName' en el tipo 'MyType'. - vldmrrdjcc

Gran respuesta de jheddings. Me gustaría mejorarlo permitiendo la referencia de matrices agregadas o colecciones de objetos, de modo que propertyName podría ser 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 object[];
                    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;
    }

Respondido el 07 de enero de 21 a las 23:01

¿Qué pasa con una lista de listas a las que accede MasterList [0] [1]? - Jesse Adam

as Array -> as object [] también da como resultado una excepción Nullreference. Lo que funciona para mí (prop no es el método más eficiente) es lanzar unknownCollection a IEnumerable y luego usar ToArray () en el resultado. violín - M1sterPl0w

Si uso el código de Ed s. yo obtengo

'ReflectionExtensions.GetProperty (Type, string)' es inaccesible debido a su nivel de protección

Parece que GetProperty() no está disponible en Xamarin.Forms. TargetFrameworkProfile is Profile7 en mi biblioteca de clases portátil (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).

Ahora encontré una solución funcional:

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

Respondido 04 Oct 18, 13:10

Solo una pequeña mejora posible. Reemplazar IF y luego regresar por: return property? .GetValue (source); - Tomino

Acerca de la discusión de propiedades anidadas, puede evitar todas las cosas de reflexión si usa el DataBinder.Eval Method (Object, String) como a continuación:

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

Por supuesto, deberá agregar una referencia al System.Web montaje, pero esto probablemente no sea un gran problema.

Respondido 29 Jul 15, 17:07

El método para llamar ha cambiado en .NET Standard (a partir de 1.6). También podemos usar el operador condicional nulo de C # 6.

using System.Reflection; 
public static object GetPropValue(object src, string propName)
{
    return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}

contestado el 25 de mayo de 17 a las 12:05

listo para usar el ? operator - blfuentes

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

Esta es una forma de obtener todas las propiedades con sus valores en una lista.

Respondido el 06 de enero de 16 a las 16:01

¿Por qué estás haciendo esto? type.GetProperty(pi.Name) cuando eso es == a la variable pi? - weston

Si está utilizando c # 6.0, deshágase de if y hacer selfValue?.ToString() De lo contrario, deshazte de if y use selfValue==null?null:selfValue.ToString() - weston

También una lista de List<KeyValuePair< es extraño, usa un diccionario Dictionary<string, string> - weston

El siguiente método funciona perfecto para mí:

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

Para obtener el valor de la propiedad:

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

Para establecer el valor de la propiedad:

t1["prop1"] = value;

Respondido 12 Feb 19, 09:02

Usando PropertyInfo del Sistema.Reflexión espacio de nombres. Reflection se compila bien sin importar a qué propiedad intentemos acceder. El error aparecerá durante el tiempo de ejecución.

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

Funciona bien para obtener la propiedad Location de un objeto

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

Obtendremos la Ubicación: {X = 71, Y = 27} También podemos devolver location.X o location.Y de la misma manera.

Respondido 23 Oct 15, 17:10

El siguiente código es un método recursivo para mostrar la jerarquía completa de todos los nombres y valores de propiedad contenidos en la instancia de un objeto. Este método utiliza una versión simplificada de AlexD's GetPropertyValue() responder arriba en este hilo. Gracias a este hilo de discusión, ¡pude descubrir cómo hacer esto!

Por ejemplo, utilizo este método para mostrar una explosión o volcado de todas las propiedades en un WebService respuesta llamando al método de la siguiente manera:

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

Respondido 06 ago 15, 11:08

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

Respondido el 29 de Septiembre de 17 a las 19:09

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"];

respondido 06 mar '18, 18:03

Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)

Respondido el 01 de diciembre de 11 a las 03:12

Aquí hay otra forma de encontrar una propiedad anidada que no requiere que la cadena le indique la ruta de anidación. Crédito a Ed S. por el método de propiedad única.

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

respondido 05 nov., 14:03

Sería mejor comprobar si Type.GetProperty devoluciones null en lugar de llamar GetValue y teniendo NullReferenceExceptions arrojado en un bucle. - Groo

Nunca mencionas qué objeto estás inspeccionando, y dado que estás rechazando los que hacen referencia a un objeto dado, asumiré que te refieres a uno estático.

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

Tenga en cuenta que marqué el objeto que se está inspeccionando con la variable local obj. null significa estático; de lo contrario, configúrelo como desee. También tenga en cuenta que el GetEntryAssembly() es uno de los pocos métodos disponibles para obtener el ensamblaje "en ejecución", es posible que desee probarlo si tiene dificultades para cargar el tipo.

Respondido 06 ago 15, 11:08

Echa un vistazo a la Heleonix.Reflexión Biblioteca. Puede obtener / establecer / invocar miembros por rutas, o crear un getter / setter (lambda compilado en un delegado) que es más rápido que la reflexión. Por ejemplo:

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

O cree un captador una vez y almacene en caché para su reutilización (esto es más eficaz pero podría generar una excepción NullReferenceException si un miembro intermedio es nulo):

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

O si quieres crear un List<Action<object, object>> de diferentes getters, solo especifique los tipos base para los delegados compilados (las conversiones de tipos se agregarán en lambdas compiladas):

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

Respondido 13 Jul 18, 13:07

nunca use bibliotecas de terceros, si puede implementarlas en su propio código en un tiempo razonable en 3-5 líneas. - Artem G

camino más corto ....

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

Respondido 13 Jul 14, 08:07

jheddings y los Alexd ambos escribieron excelentes respuestas sobre cómo resolver cadenas de propiedades. Me gustaría incluir la mía en la mezcla, ya que escribí una biblioteca dedicada exactamente para ese propósito.

Pather.CSharpla clase principal es Resolver. De forma predeterminada, puede resolver propiedades, matrices y entradas de diccionario.

Entonces, por ejemplo, si tiene un objeto como este

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

y quiero conseguir Property2, puedes hacerlo así:

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

Este es el ejemplo más básico de las rutas que puede resolver. Si desea ver qué más puede, o cómo puede extenderlo, simplemente diríjase a su Página de Github.

contestado el 23 de mayo de 17 a las 14:05

Aquí está mi solución. También funciona con objetos COM y permite acceder a elementos de colección / matriz desde objetos 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;
}

Respondido 24 Feb 18, 17:02

Esto es lo que obtuve en base a otras respuestas. Un poco exagerado al ser tan específico con el manejo de errores.

public static T GetPropertyValue<T>(object sourceInstance, string targetPropertyName, bool throwExceptionIfNotExists = false)
{
    string errorMsg = null;

    try
    {
        if (sourceInstance == null || string.IsNullOrWhiteSpace(targetPropertyName))
        {
            errorMsg = $"Source object is null or property name is null or whitespace. '{targetPropertyName}'";
            Log.Warn(errorMsg);

            if (throwExceptionIfNotExists)
                throw new ArgumentException(errorMsg);
            else
                return default(T);
        }

        Type returnType = typeof(T);
        Type sourceType = sourceInstance.GetType();

        PropertyInfo propertyInfo = sourceType.GetProperty(targetPropertyName, returnType);
        if (propertyInfo == null)
        {
            errorMsg = $"Property name '{targetPropertyName}' of type '{returnType}' not found for source object of type '{sourceType}'";
            Log.Warn(errorMsg);

            if (throwExceptionIfNotExists)
                throw new ArgumentException(errorMsg);
            else
                return default(T);
        }

        return (T)propertyInfo.GetValue(sourceInstance, null);
    }
    catch(Exception ex)
    {
        errorMsg = $"Problem getting property name '{targetPropertyName}' from source instance.";
        Log.Error(errorMsg, ex);

        if (throwExceptionIfNotExists)
            throw;
    }

    return default(T);
}

Respondido 26 Abr '19, 22:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.