Existe-t-il un moyen en C # où je puisse utiliser la réflexion pour définir une propriété d'objet?
Ex:
MyObject obj = new MyObject();
obj.Name = "Value";
Je veux définir obj.Name
avec réflexion. Quelque chose comme:
Reflection.SetProperty(obj, "Name") = "Value";
Y-a-t'il une façon de le faire?
Oui, vous pouvez utiliser Type.InvokeMember()
:
using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
Type.DefaultBinder, obj, "Value");
Cela lève une exception si obj
ne possède pas de propriété appelée Name
, ou ne peut pas être défini.
Une autre approche consiste à obtenir les métadonnées de la propriété, puis à les définir. Cela vous permettra de vérifier l'existence de la propriété et de vérifier qu'elle peut être définie:
using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
prop.SetValue(obj, "Value", null);
}
Vous pouvez aussi faire:
Type type = target.GetType();
PropertyInfo prop = type.GetProperty("propertyName");
prop.SetValue (target, propertyValue, null);
où cible est l'objet qui aura sa propriété définie.
Réflexion, fondamentalement, c'est-à-dire.
myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);
ou il existe des bibliothèques pour aider à la fois en termes de commodité et de performance; par exemple avec FastMember :
var wrapped = ObjectAccessor.Create(obj);
wrapped[property] = "Bob";
(qui a aussi l'avantage de ne pas avoir besoin de savoir à l'avance si c'est un terrain ou une propriété)
Ou vous pouvez envelopper la doublure de Marc dans votre propre classe d'extension:
public static class PropertyExtension{
public static void SetPropertyValue(this object obj, string propName, object value)
{
obj.GetType().GetProperty(propName).SetValue(obj, value, null);
}
}
et appelez comme ça:
myObject.SetPropertyValue("myProperty", "myValue");
Pour faire bonne mesure, ajoutons une méthode pour obtenir une valeur de propriété:
public static object GetPropertyValue(this object obj, string propName)
{
return obj.GetType().GetProperty(propName).GetValue (obj, null);
}
Oui, en utilisant System.Reflection
:
using System.Reflection;
...
string prop = "name";
PropertyInfo pi = myObject.GetType().GetProperty(prop);
pi.SetValue(myObject, "Bob", null);
Vous pouvez également accéder aux champs de la même manière:
var obj=new MyObject();
FieldInfo fi = obj.GetType().
GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)
Avec la réflexion, tout peut être un livre ouvert :) Dans mon exemple, nous lions à un champ de niveau instance privé.
Utilisez quelque chose comme ça:
public static class PropertyExtension{
public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
{
PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
}
}
ou
public static class PropertyExtension{
public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
{
PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeValue = (value == null) ? null : Convert.ChangeType(value, t);
property.SetValue(p_object, safeValue, null);
}
}
Vous pouvez essayer ceci lorsque vous souhaitez affecter en masse les propriétés d'un objet à partir d'un autre objet à l'aide des noms de propriété:
public static void Assign(this object destination, object source)
{
if (destination is IEnumerable && source is IEnumerable)
{
var dest_enumerator = (destination as IEnumerable).GetEnumerator();
var src_enumerator = (source as IEnumerable).GetEnumerator();
while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
dest_enumerator.Current.Assign(src_enumerator.Current);
}
else
{
var destProperties = destination.GetType().GetProperties();
foreach (var sourceProperty in source.GetType().GetProperties())
{
foreach (var destProperty in destProperties)
{
if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
destProperty.SetValue(destination, sourceProperty.GetValue(source, new object[] { }), new object[] { });
break;
}
}
}
}
Je viens de publier un paquet Nuget qui permet de définir non seulement les propriétés de premier niveau, mais également les propriétés imbriquées dans l'objet donné.
Voici le paquet
Définit la valeur d'une propriété d'un objet en fonction de son chemin d'accès depuis la racine.
L'objet peut être un objet complexe et la propriété peut être une propriété imbriquée profonde à plusieurs niveaux ou une propriété directement sous la racine. ObjectWriter
trouvera la propriété à l'aide du paramètre property path et mettra à jour sa valeur. Property path est le nom ajouté des propriétés visitées de la racine à la propriété du noeud final que nous voulons définir, délimité par le paramètre de chaîne de délimiteur.
Usage:
Pour configurer les propriétés directement sous la racine de l'objet:
C'est à dire. LineItem
la classe a une propriété int appelée ItemId
LineItem lineItem = new LineItem();
ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);
Pour configurer une propriété imbriquée, plusieurs niveaux sous la racine de l'objet:
C'est à dire. Invite
class a une propriété appelée State
, qui a une propriété appelée Invite
(de type Invite), qui a une propriété appelée Recipient
, qui a une propriété appelée Id
.
Pour rendre les choses encore plus complexes, la propriété State
n'est pas un type de référence, c'est un struct
.
Voici comment définir la propriété Id (avec la valeur de chaîne "Outlook") au bas de l'arborescence des objets sur une seule ligne.
Invite invite = new Invite();
ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "Outlook", delimiter: "_");
Sur la suggestion de MarcGravell, j'ai construit la méthode statique suivante. La méthode génériquement affecte toutes les propriétés correspondantes de l'objet source à la cible à l'aide de FastMember
public static void DynamicPropertySet(object source, object target)
{
//SOURCE
var src_accessor = TypeAccessor.Create(source.GetType());
if (src_accessor == null)
{
throw new ApplicationException("Could not create accessor!");
}
var src_members = src_accessor.GetMembers();
if (src_members == null)
{
throw new ApplicationException("Could not fetch members!");
}
var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
var src_class_propNames = src_class_members.Select(x => x.Name);
var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);
//TARGET
var trg_accessor = TypeAccessor.Create(target.GetType());
if (trg_accessor == null)
{
throw new ApplicationException("Could not create accessor!");
}
var trg_members = trg_accessor.GetMembers();
if (trg_members == null)
{
throw new ApplicationException("Could not create accessor!");
}
var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
var trg_class_propNames = trg_class_members.Select(x => x.Name);
var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);
var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
var propNames = trg_propNames.Intersect(src_propNames);
foreach (var propName in propNames)
{
trg_accessor[target, propName] = src_accessor[source, propName];
}
foreach (var member in class_propNames)
{
var src = src_accessor[source, member];
var trg = trg_accessor[target, member];
if (src != null && trg != null)
{
DynamicPropertySet(src, trg);
}
}
}