J'essaie de mettre en œuvre la transformation de données en utilisant Reflection1 exemple dans mon code.
La fonction GetSourceValue
a un commutateur comparant différents types, mais je souhaite supprimer ces types et propriétés et permettre à GetSourceValue
d'obtenir la valeur de la propriété en utilisant une seule chaîne en tant que paramètre. Je veux passer une classe et une propriété dans la chaîne et résoudre la valeur de la propriété.
Est-ce possible?
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
Bien sûr, vous voudrez ajouter une validation, etc., mais c’est l’essentiel.
Que diriez-vous quelque chose comme ça:
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 seule chaîne, 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 en tant que méthodes ou extensions statiques.
Ajoutez à n'importe quelle 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"];
Qu'en est-il de l'utilisation de 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 alors
Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
Grande réponse par jheddings. J'aimerais l'améliorer pour permettre le référencement de tableaux ou de collections d'objets agrégés, afin que propertyName puisse être 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;
}
Si j'utilise le code de Ed S. je reçois
'ReflectionExtensions.GetProperty (Type, chaîne)' est inaccessible en raison de son niveau de protection
Il semble que GetProperty()
ne soit pas disponible dans Xamarin.Forms. TargetFrameworkProfile
est Profile7
dans ma bibliothèque de classes portable (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.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);
}
À propos de la discussion sur les propriétés imbriquées, vous pouvez éviter tout problème 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 aurez besoin d'ajouter une référence à l'assembly System.Web
, mais ce n'est probablement pas un gros problème.
La méthode à appeler a changé dans .NET Standard (à partir de 1.6). Nous pouvons également utiliser l'opérateur conditionnel nul de C # 6.
using System.Reflection;
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
Utilisation de PropertyInfo de l’espace System.Reflection namespace. La réflexion compile parfaitement quelle que soit la propriété à laquelle nous essayons d’accéder. L'erreur se produira pendant 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;
}
Cela fonctionne bien pour obtenir la propriété Location d'un objet
Label1.Text = GetObjProperty(button1, "Location").ToString();
Nous aurons l'emplacement: {X = 71, Y = 27} Nous pouvons également retourner emplacement.X ou emplacement.Y de la même manière.
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.
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"];
Voici un autre moyen de rechercher une propriété imbriquée ne nécessitant pas de chaîne pour vous indiquer le chemin d'imbrication. 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);
}
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);
}
Vous ne mentionnez jamais l'objet que vous inspectez, et comme vous rejetez ceux qui font référence à un objet donné, je suppose que vous voulez dire 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 qui est inspecté avec la variable locale obj
. null
signifie statique, sinon définissez-le comme vous le souhaitez. Notez également que la fonction GetEntryAssembly()
est l’une des méthodes disponibles pour obtenir l’assemblage "en cours". Vous voudrez peut-être jouer avec elle si vous avez du mal à charger le type.
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
manière plus courte ....
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());
jheddings et AlexD ont tous deux d'excellentes réponses sur la façon de résoudre les chaînes de propriétés. J'aimerais ajouter le mien à la composition, car j'ai écrit une bibliothèque dédiée exactement à cette fin.
La classe principale de Pather.CSharp est Resolver
. Par défaut, il peut résoudre les propriétés, les entrées de tableau et de dictionnaire.
Donc, par exemple, si vous avez un objet comme celui-ci
var o = new { Property1 = new { Property2 = "value" } };
et 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 simple des chemins qu'il peut résoudre. Si vous voulez voir quoi d’autre, ou comment vous pouvez l’agrandir, dirigez-vous simplement vers la page Github .
Jetez un coup d’œil à la bibliothèque Heleonix.Reflection . Vous pouvez obtenir/définir/invoquer des membres par des chemins ou créer un getter/setter (lambda compilé en délégué) plus rapide que la réflexion. Par exemple:
var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);
Ou créez un getter une fois et mettez-le en cache pour le réutiliser (ceci est plus performant mais peut générer une exception NullReferenceException si un membre intermédiaire est null):
var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Ou si vous souhaitez créer un List<Action<object, object>>
de différents getters, spécifiez simplement les types de base des délégués compilés (les conversions de types seront ajoutées aux lambdas compilés):
var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Voici ce que j'ai obtenu à partir d'autres réponses. Un peu exagéré de devenir si spécifique avec la gestion des erreurs.
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);
}
Voici ma solution. Il fonctionne également avec les objets COM et permet d'accéder aux éléments de collection/tableau à partir d'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;
}
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 définir la valeur de la propriété:
t1["prop1"] = value;