Obtenir la valeur de la propriété de la chaîne en utilisant la réflexion en C#

j'essaie d'implémenter la transformation de données en utilisant la réflexion 1 exemple dans mon code.

la fonction GetSourceValue dispose d'un commutateur comparant différents types, mais je veux supprimer ces types et propriétés et avoir GetSourceValue obtenir la valeur de la propriété en utilisant seulement une chaîne simple comme paramètre. Je veux passer d'une classe et de la propriété dans la chaîne et régler la valeur de la propriété.

est-ce possible?

1 Web version Archive de blog original

700
demandé sur GlenH7 2009-07-29 01:58:51

21 réponses

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

bien sûr, vous voudrez ajouter validation et autres, mais c'est l'essentiel de celui-ci.

1399
répondu Ed S. 2013-05-03 23:12:20

Que Diriez-vous de quelque chose comme ceci:

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

cela vous permettra de descendre dans les propriétés en utilisant une chaîne simple, comme ceci:

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

Vous pouvez utiliser ces méthodes comme les méthodes statiques ou des extensions.

176
répondu jheddings 2012-11-08 15:23:00

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

ensuite, vous pouvez utiliser comme:

Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];
47
répondu Eduardo Cuomo 2014-07-23 19:58:10

Qu'en est-il de l'utilisation du CallByName de l'espace de noms Microsoft.VisualBasic ( Microsoft.VisualBasic.dll )? Il utilise la réflexion pour obtenir les propriétés, les champs et les méthodes des objets normaux, des objets COM, et même des objets dynamiques.

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

et puis

Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
39
répondu Fredou 2014-11-02 18:04:26

grande réponse de jheddings. Je voudrais l'améliorer en permettant le référencement des tableaux agrégés ou des collections d'objets, de sorte que propertyName pourrait être property1.propriété2[X].propriété3:

    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;
    }
23
répondu AlexD 2014-09-02 21:53:53

si j'utilise le code de Ed S. I get

'extensions de réflection.GetProperty (Type, chaîne)' est inaccessible en raison de son niveau de protection

il semble que GetProperty() ne soit pas disponible en Xamarin.Forme. TargetFrameworkProfile est Profile7 dans ma bibliothèque de classe Portable (.net Framework 4.5, Windows 8, ASP.NET noyau 1.0, Xamarine.Android, Xamarin.iOS, Xamarin.iOS Classic).

maintenant j'ai trouvé une solution de travail:

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
répondu testing 2018-10-04 10:18:44

à propos de la discussion sur les propriétés imbriquées, vous pouvez éviter tous les trucs de réflexion si vous utilisez le DataBinder.Eval Method (Object, String) comme ci-dessous:

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

bien sûr, vous devrez ajouter une référence à l'Assemblée System.Web , mais ce n'est probablement pas une grosse affaire.

6
répondu Rubens Farias 2015-07-29 14:19:48

utilisant PropertyInfo du système .La réflexion espace de noms. La réflexion se compile très bien, peu importe la propriété à laquelle nous tentons d'accéder. L'erreur lors de l'exécution.

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

Il fonctionne très bien pour obtenir l'Emplacement de la propriété d'un objet

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

on aura L'emplacement: {X = 71,Y = 27} Nous pouvons également retourner l'emplacement.X ou de l'emplacement.Y sur le même chemin.

4
répondu A Ghazal 2015-10-23 14:47:22

la méthode d'appel a changé dans la norme .NET (à partir de 1.6). Nous pouvons aussi utiliser l'opérateur null conditional de C# 6.

using System.Reflection; 
public static object GetPropValue(object src, string propName)
{
    return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
4
répondu Matt Frear 2017-05-25 09:13:23
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;
    }

C'est un moyen d'obtenir toutes les propriétés avec leurs valeurs dans une Liste.

3
répondu Бончо Вълков 2016-01-06 13:46:24

Voici une autre façon de trouver une propriété imbriquée qui ne nécessite pas la chaîne pour vous dire le chemin de nidification. Crédit à Ed S. pour la méthode de la propriété unique.

    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);
    }
2
répondu Recursor 2014-11-05 00:38:13

vous ne mentionnez jamais quel objet vous inspectez, et puisque vous rejetez ceux qui font référence à un objet donné, je supposerai que vous voulez dire un objet statique.

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

notez que j'ai marqué l'objet inspecté avec la variable locale obj . null signifie statique, contraire à ce que vous désirez. Notez également que le GetEntryAssembly() est l'un des quelques méthodes disponibles pour obtenir la "gestion" de l'assemblée, vous pouvez jouer avec elle si vous avez du mal à charger le type.

2
répondu Guvante 2015-08-06 08:56:27

le code suivant est une méthode récursive pour afficher la hiérarchie entière de tous les noms et valeurs de propriétés contenus dans l'instance d'un objet. Cette méthode utilise une version simplifiée de la réponse GetPropertyValue() d'AlexD ci-dessus dans ce thread. Grâce à ce fil de discussion, j'ai pu trouver comment faire cela!

Par exemple, j'utilise cette méthode pour montrer une explosion ou un dump de toutes les propriétés dans un WebService réponse en appelant la méthode comme suit:

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);
    }
  }
}
2
répondu gridtrak 2015-08-06 08:56:58
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"];
2
répondu Komal Narang 2018-03-06 15:06:15
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
1
répondu Kyle 2011-11-30 23:55:28

chemin court ....

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());
1
répondu Budiantowang 2014-07-13 04:34:29

jheddings et AlexD tous deux ont écrit d'excellentes réponses sur la façon de résoudre les chaînes de propriété. J'aimerais mettre le mien dans le mix, puisque j'ai écrit une bibliothèque dédiée exactement pour cela.

Path.La classe principale de CSharp est Resolver . Par défaut, il peut résoudre des propriétés, des tableaux et des entrées de dictionnaires.

Ainsi, par exemple, si vous avez un objet comme celui-ci

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

et si vous voulez obtenir Property2 , vous pouvez le faire comme ceci:

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

c'est l'exemple le plus basique des chemins qu'il peut résoudre. Si vous voulez voir ce qu'il peut, ou comment vous pouvez l'étendre, juste la tête pour son Github page .

1
répondu Domysee 2017-05-23 11:47:26
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);
}
1
répondu Rahma Sammaron 2017-09-29 16:22:41

voici ma solution. Il fonctionne également avec des objets COM et permet d'accéder à la collection/les éléments de tableau des objets 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;
}
0
répondu user3175253 2018-02-24 14:24:34

La méthode ci-dessous fonctionne parfaitement pour moi:

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

pour obtenir la valeur de la propriété:

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

pour fixer la valeur de la propriété:

t1["prop1] = value;
0
répondu Derrick.X 2018-07-09 05:19:07

regardez le Heleonix.Réflexion bibliothèque. Vous pouvez obtenir/définir/invoquer des membres par des chemins, ou créer un getter/setter (lambda compilé dans un délégué) qui est plus rapide que la réflexion. Par exemple:

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

ou créer un getter une fois et un cache pour la réutilisation (c'est plus performant mais pourrait jeter NullReferenceException si un membre intermédiaire est null):

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

Ou si vous voulez créer un List<Action<object, object>> de différents getters, il suffit de spécifier les types de base pour les délégués compilés (les conversions de type seront ajoutées dans lambdas compilé):

var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
0
répondu Hennadii Lutsyshyn 2018-07-13 10:57:55