web-dev-qa-db-fra.com

Définir la propriété de l'objet à l'aide de la réflexion

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?

299
Melursus

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

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.

269
El Cheicon

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é)

86
Marc Gravell

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);
}
26
Erik K.

Oui, en utilisant System.Reflection:

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);
14
D Stanley

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é.

12
JoshBerke

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);
   }
}
11
Ardalan Shahgholi

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

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: "_");
0
Dogu Arslan

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