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

323

Existe-t-il un moyen en C # où je peux utiliser la réflexion pour définir une propriété d'objet?

Ex:

MyObject obj = new MyObject();
obj.Name = "Value";

Je veux régler obj.Nameavec réflexion. Quelque chose comme:

Reflection.SetProperty(obj, "Name") = "Value";

Y-a-t'il une façon de le faire?

Melursus
la source

Réponses:

391

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èvera une exception si objaucune propriété n'est appelée Nameou si elle ne peut pas être définie.

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);
}
Andy
la source
73
Si vous ne traitez pas toutes les chaînes, vous voudrez peut-être d'abord convertir les données: var val = Convert.ChangeType(propValue, propInfo.PropertyType); source: devx.com/vb2themax/Tip/19599
LostNomad311
4
vous pouvez également utiliser obj.GetType().GetProperty("Name")?.GetSetMethod()?.Invoke(...)
tecfield
1
il est impossible de définir une valeur pour les CanWrite=Falsetypes, non?
T.Todua
287

Vous pouvez également 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.

El Cheicon
la source
11
J'ai fait la même chose aujourd'hui. Ce qui précède fonctionne très bien, il est évident qu'une vérification nulle doit être effectuée sur l'accessoire avant d'essayer de l'utiliser.
Antony Scott
3
@AntonyScott Je pense que vous voudriez savoir si vous invoquez la mauvaise propriété, donc "échouer en silence" semble être un mauvais choix.
jih
3
@jih Je peux voir votre point, mais cela dépend vraiment de la situation.
Antony Scott
94

Réflexion, essentiellement, à savoir

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

ou il existe des bibliothèques pour aider à la fois en termes de commodité et de performances; par exemple avec FastMember :

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

(qui a également l'avantage de ne pas avoir besoin de savoir à l'avance s'il s'agit d'un champ par rapport à une propriété)

Marc Gravell
la source
Wow, je suis un peu confus de la fusion, mais j'ai retrouvé votre réponse! Merci, vous méritez une acceptation, mais puisque mon fil est fusionné :( Merci encore!
halfpastfour.am
@MarcGravell, je regardais FastMember et c'est assez intéressant. Y a-t-il un démarrage / tutoriel quelque part pour nous, simples mortels, pour utiliser votre grande bibliothèque?
Sudhanshu Mishra
Comment puis-je obtenir le type de propriété par FastMember?
Dit Roohullah Allem le
@Jahan accessor => GetMembers => Member => Type
Marc Gravell
très bonne réponse! La bonne chose à propos d'un oneliner est qu'il est beaucoup plus rapide à comprendre, car il n'y a pas de variables nommées par l'utilisateur entre lesquelles cela pourrait ne pas avoir de sens ..
Bastiaan
27

Ou vous pouvez envelopper un liner 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-le comme ceci:

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);
}
Erik K.
la source
14

Oui, en utilisant System.Reflection:

using System.Reflection;

...

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

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);
   }
}
Ardalan Shahgholi
la source
2
La partie où vous obtenez le type de propriété, puis le cast, m'a été très utile. Il fonctionne comme un charme. Merci
Marc
12

Vous pouvez également accéder aux champs de manière similaire:

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

Avec réflexion, tout peut être un livre ouvert :) Dans mon exemple, nous nous lions à un champ de niveau d'instance privé.

JoshBerke
la source
8

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;
            }
        }
    }
}
user3679106
la source
2
Salut et bienvenue sur Stack Overflow. Veuillez formater votre code correctement et ne pas simplement le vider dans votre message, cela aidera les autres à comprendre votre réponse.
ThreeFx
0

Je viens de publier un package Nuget qui permet de configurer non seulement les propriétés de premier niveau mais également les propriétés imbriquées dans l'objet donné à n'importe quelle profondeur.

Voici le package

Définit la valeur d'une propriété d'un objet par son chemin 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 elle peut être une propriété directement sous la racine. ObjectWritertrouvera la propriété en utilisant le paramètre de chemin de propriété et mettra à jour sa valeur. Le chemin de propriété est le nom ajouté des propriétés visitées de la racine à la propriété de noeud final que nous voulons définir, délimitées 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. LineItemla classe a une propriété int appeléeItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

Pour configurer plusieurs niveaux de propriété imbriquée sous la racine de l'objet:

C'est à dire. Inviteclasse 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éeId .

Pour rendre les choses encore plus complexes, la Statepropriété n'est pas un type de référence, c'est unstruct .

Voici comment vous pouvez définir la propriété Id (sur la valeur de chaîne de «outlook») au bas de l'arborescence d'objets sur une seule ligne.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");
Dogu Arslan
la source
0

Sur la base de la suggestion de MarcGravell, j'ai construit la méthode statique suivante: la méthode attribue de manière générique 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);
            }
        }
    }
Convaincant
la source