Eigenschapswaarde ophalen uit string met reflectie

Ik probeer de Datatransformatie te implementeren met Reflection< sup>1voorbeeld in mijn code.

De functie GetSourceValueheeft een schakelaar waarmee verschillende typen worden vergeleken, maar ik wil deze typen en eigenschappen verwijderen en GetSourceValuede waarde van de eigenschap laten ophalen met slechts een enkele tekenreeks als de parameter. Ik wil een klasse en eigenschap in de string doorgeven en de waarde van de eigenschap oplossen.

Is dit mogelijk?

1Webarchiefversie van originele blogpost


Antwoord 1, autoriteit 100%

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

Natuurlijk wil je validatie toevoegen en zo, maar dat is de essentie.


Antwoord 2, autoriteit 11%

Wat dacht je van zoiets:

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

Hiermee kunt u dalen in eigendommen met behulp van een enkele tekenreeks, zoals deze:

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

U kunt deze methoden gebruiken als statische methoden of extensies.


3, Autoriteit 4%

Voeg toe aan elke 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; }
}

Dan kunt u gebruiken als:

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

4, Autoriteit 2%

Hoe zit het met het gebruik van de CallByNamevan de Microsoft.VisualBasicnamespace (Microsoft.VisualBasic.dll)? Het maakt gebruik van reflectie om eigenschappen, velden en methoden van normale objecten, COM-objecten en zelfs dynamische objecten te krijgen.

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

en dan

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

Antwoord 5

De methode om te bellen is gewijzigd in .NET Standard (vanaf 1.6). We kunnen ook de null-voorwaardelijke operator van C# 6 gebruiken.

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

Antwoord 6

Over de discussie over geneste eigenschappen kun je alle reflecties vermijden als je de DataBinder.Eval Method (Object, String)zoals hieronder:

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

Natuurlijk moet je een verwijzing naar de System.Web-assembly toevoegen, maar dit is waarschijnlijk niet erg.


Antwoord 7

De onderstaande methode werkt perfect voor mij:

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

Om de waarde van het onroerend goed te krijgen:

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

Om de eigenschapswaarde in te stellen:

t1["prop1"] = value;

8

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

Dit is een manier om alle eigendommen met hun waarden in een lijst te krijgen.


9

Gebruik van PropertyInfo van het System.reflection NameSpace. Reflectie compileert prima, ongeacht welk eigendom we proberen toegang te krijgen. De fout zal tijdens de looptijd omhoog komen.

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

Het werkt prima om de locatie van de locatie van een object

te krijgen

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

We krijgen de locatie: {X=71,Y=27}
We kunnen locatie.X of locatie.Y ook op dezelfde manier retourneren.


Antwoord 10

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

Antwoord 11

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

Antwoord 12

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

Antwoord 13

Je vermeldt nooit welk object je inspecteert, en aangezien je degene die verwijzen naar een bepaald object afwijst, neem ik aan dat je een statisch object bedoelt.

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

Merk op dat ik het object dat wordt geïnspecteerd gemarkeerd met de lokale variabele obj. nullbetekent statisch, stel het anders in op wat je wilt. Merk ook op dat de GetEntryAssembly()een van de weinige beschikbare methoden is om de “running” assembly te krijgen, misschien wil je ermee spelen als je het moeilijk vindt om het type te laden.


Antwoord 14

De volgende code is een recursieve methode voor het weergeven van de volledige hiërarchie van alle eigenschapsnamen en -waarden in de instantie van een object. Deze methode gebruikt een vereenvoudigde versie van AlexD’s GetPropertyValue()-antwoord hierboven in deze thread. Dankzij deze discussiethread kon ik erachter komen hoe ik dit moest doen!

Ik gebruik deze methode bijvoorbeeld om een explosie of dump van alle eigenschappen in een WebService-reactie te tonen door de methode als volgt aan te roepen:

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

Antwoord 15

Bekijk de Heleonix.Reflectionbibliotheek. Je kunt leden ophalen/instellen/aanroepen via paden, of een getter/setter maken (lambda gecompileerd in een afgevaardigde) die sneller is dan reflectie. Bijvoorbeeld:

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

Of maak één keer een getter en cache voor hergebruik (dit is beter, maar kan NullReferenceException genereren als een tussenliggend lid null is):

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

Of als u een List<Action<object, object>>van verschillende getters wilt maken, geeft u gewoon de basistypen op voor gecompileerde afgevaardigden (typeconversies worden toegevoegd aan gecompileerde lambda’s):

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

Antwoord 16

Hier is een andere manier om een geneste eigenschap te vinden waarvoor de string niet nodig is om u het geneste pad te vertellen. Met dank aan Ed S. voor de single property-methode.

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

17

Shorter Way ….

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

Hier is mijn oplossing. Het werkt ook met COM-objecten en maakt toegang tot het verzamelen / array-items van COM-objecten.

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

Antwoord 19

Dit is wat ik heb gekregen op basis van andere antwoorden. Een beetje overdreven om zo specifiek te worden met de foutafhandeling.

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

Other episodes