it-swarm.com.de

Abrufen des Eigenschaftswerts von String mithilfe von Reflektion in C #

Ich versuche die Datentransformation mit Reflection zu implementieren1 Beispiel in meinem Code.

Die GetSourceValue-Funktion hat einen Schalter, der verschiedene Typen vergleicht. Ich möchte jedoch diese Typen und Eigenschaften entfernen und GetSourceValue den Wert der Eigenschaft mit nur einer einzigen Zeichenfolge als Parameter abrufen. Ich möchte eine Klasse und eine Eigenschaft in der Zeichenfolge übergeben und den Wert der Eigenschaft auflösen.

Ist das möglich?

1Webarchiv-Version des ursprünglichen Blogbeitrags

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

Natürlich werden Sie Validierung und Whatnot hinzufügen wollen, aber das ist der Kernpunkt davon.

1494
Ed S.

Wie wäre es mit so etwas:

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

Auf diese Weise können Sie mit einer einzigen Zeichenfolge wie folgt in Eigenschaften absteigen:

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

Sie können diese Methoden entweder als statische Methoden oder als Erweiterungen verwenden.

188
jheddings

Zu beliebiger Class hinzufügen:

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

Dann können Sie verwenden als:

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

Was ist mit der CallByName des Microsoft.VisualBasic-Namespace (Microsoft.VisualBasic.dll)? Es verwendet Reflektion, um Eigenschaften, Felder und Methoden von normalen Objekten, COM-Objekten und sogar dynamischen Objekten abzurufen.

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

und dann

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

Große Antwort von jheddings. Ich möchte es verbessern, indem ich auf aggregierte Arrays oder Sammlungen von Objekten referenzieren kann, so dass propertyName property1.property2 [X] .property3 sein könnte:

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

Wenn ich den Code von Ed S. benutze, bekomme ich

Auf 'ReflectionExtensions.GetProperty (Type, string)' kann aufgrund seiner Schutzstufe nicht zugegriffen werden

Es scheint, dass GetProperty() in Xamarin.Forms nicht verfügbar ist. TargetFrameworkProfile ist Profile7 in meiner Portable Class Library (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).

Nun habe ich eine funktionierende Lösung gefunden:

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

Quelle

11
testing

In der geschachtelten Eigenschaftsdiskussion können Sie alle Überlegungen vermeiden, wenn Sie die DataBinder.Eval Method (Object, String) wie folgt verwenden:

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

Natürlich müssen Sie einen Verweis auf die System.Web Assembly hinzufügen, aber das ist wahrscheinlich keine große Sache.

9
Rubens Farias

Die aufzurufende Methode hat sich in .NET Standard (ab 1.6) geändert. Wir können auch den bedingungslosen Operator von C # 6 verwenden.

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

Verwenden von PropertyInfo des Namespaces System.Reflection . Reflection kompiliert gut, egal auf welches Objekt wir zugreifen möchten. Der Fehler wird während der Laufzeit angezeigt. 

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

Es funktioniert gut, um die Location-Eigenschaft eines Objekts abzurufen

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

Wir erhalten den Standort: {X = 71, Y = 27} Wir können auch location.X oder location.Y auf dem gleichen Weg zurückgeben.

4
A Ghazal
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;
    }

Dies ist eine Möglichkeit, alle Eigenschaften mit ihren Werten in einer Liste abzurufen.

Der folgende Code ist eine rekursive Methode zum Anzeigen der gesamten Hierarchie aller in der Instanz eines Objekts enthaltenen Eigenschaftsnamen und -werte. Diese Methode verwendet eine vereinfachte Version von AlexDs GetPropertyValue() Antwort in diesem Thread. Dank dieses Diskussionsthreads konnte ich herausfinden, wie das geht!

Ich verwende diese Methode beispielsweise, um eine Explosion oder einen Speicherauszug aller Eigenschaften in einer Antwort auf WebService anzuzeigen, indem ich die Methode wie folgt aufrufe:

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
gridtrak
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
Komal Narang

Hier finden Sie eine weitere Möglichkeit, eine verschachtelte Eigenschaft zu finden, für die Sie nicht den String angeben müssen, der den Verschachtelungspfad angibt. Dank an Ed S. für die Methode der einzelnen Immobilien.

    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
Recursor
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);
}
2
Rahma Sammaron

Sie erwähnen niemals, welches Objekt Sie untersuchen, und da Sie Objekte ablehnen, die auf ein bestimmtes Objekt verweisen, gehe ich davon aus, dass Sie ein statisches Objekt meinen.

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

Beachten Sie, dass ich das zu inspizierende Objekt mit der lokalen Variablen obj markiert habe. null bedeutet statisch, andernfalls setzen Sie es auf das, was Sie möchten. Beachten Sie auch, dass GetEntryAssembly() eine der wenigen verfügbaren Methoden ist, um die Assembly in Gang zu bringen. Vielleicht möchten Sie damit herumspielen, wenn Sie den Typ schwer laden.

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

kürzerer Weg ....

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
Budiantowang

jheddings und AlexD beide haben hervorragende Antworten zum Auflösen von Eigenschaftszeichenfolgen geschrieben. Ich möchte meine gerne in die Mischung einfließen lassen, da ich genau dafür eine eigene Bibliothek geschrieben habe.

Pather.CSharp s Hauptklasse ist Resolver. Standardmäßig können Eigenschaften, Array- und Wörterbucheinträge aufgelöst werden.

So zum Beispiel, wenn Sie ein Objekt wie dieses haben

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

und Property2 bekommen wollen, können Sie es so machen:

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

Dies ist das einfachste Beispiel für die Pfade, die es auflösen kann. Wenn Sie sehen möchten, was es noch kann oder wie Sie es erweitern können, besuchen Sie einfach seine Github-Seite .

1
Domysee

Schauen Sie sich die Bibliothek Heleonix.Reflection an. Sie können Mitglieder über Pfade abrufen/einstellen/aufrufen oder einen Getter/Setter (in einen Delegaten kompilierter Lambda) erstellen, der schneller ist als die Reflexion. Zum Beispiel:

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

Oder erstellen Sie einmalig einen Getter und den Cache für die Wiederverwendung (dies ist performanter, kann jedoch die NullReferenceException auslösen, wenn ein Zwischenmitglied null ist)

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

Wenn Sie einen List<Action<object, object>> verschiedener Getter erstellen möchten, geben Sie einfach Basistypen für kompilierte Delegaten an (Typumwandlungen werden in kompilierte Lambdas eingefügt):

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

Hier ist, was ich basierend auf anderen Antworten bekommen habe. Ein wenig übertrieben, wenn man sich mit der Fehlerbehandlung so genau auseinandersetzt.

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);
}
0
Jeff Codes

Hier ist meine Lösung. Es funktioniert auch mit COM-Objekten und ermöglicht den Zugriff auf Sammlungs-/Arrayelemente von COM-Objekten.

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
user3175253

Die untenstehende Methode funktioniert für mich perfekt:

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

So erhalten Sie den Eigenschaftswert:

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

So legen Sie den Eigenschaftswert fest:

t1["prop1"] = value;
0
Derrick.X