web-dev-qa-db-fra.com

Accès à la propriété d'objet en tant que chaîne et définition de sa valeur

J'ai une instance de la classe Account. Chaque objet de compte a un propriétaire, une référence, etc.

Une façon d'accéder aux propriétés d'un compte est d'utiliser des accesseurs comme

account.Reference;

mais j'aimerais pouvoir y accéder en utilisant des sélecteurs de chaînes dynamiques comme:

account["PropertyName"];

comme en JavaScript. J'aurais donc account["Reference"] qui retournerait la valeur, mais j'aimerais aussi pouvoir assigner une nouvelle valeur après ça comme:

account["Reference"] = "124ds4EE2s";

J'ai remarqué que je peux utiliser

DataBinder.Eval(account,"Reference") 

pour obtenir une propriété basée sur une chaîne, mais en utilisant cela, je ne peux pas attribuer de valeur à la propriété.

Une idée sur comment je pourrais faire ça?

43
zanona

Tout d'abord, vous devez éviter de l'utiliser; C # est un langage fortement typé, alors profitez des avantages de sécurité et de performances de type qui accompagnent cet aspect.

Si vous avez une raison légitime d'obtenir et de définir dynamiquement la valeur d'une propriété (en d'autres termes, lorsque le type et/ou le nom de la propriété ne peut pas être défini dans le code), vous devrez utiliser la réflexion.

La façon la plus en ligne serait la suivante:

object value = typeof(YourType).GetProperty("PropertyName").GetValue(yourInstance);
...
typeof(YourType).GetProperty("PropertyName").SetValue(yourInstance, "value");

Cependant, vous pouvez mettre en cache l'objet PropertyInfo pour le rendre plus lisible:

System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");

object value = prop.GetValue(yourInstance);
...
prop.SetValue(yourInstance, "value");
66
Adam Robinson

Vous pouvez essayer de combiner l'indexeur avec la réflexion ...

public object this[string propertyName]
{
    get
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        return property.GetValue(this, null);
    }
    set
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        property.SetValue(this,value, null);
    }
}
32
Richard Friend

Si vous utilisez .Net 4, vous pouvez utiliser le mot-clé dynamique maintenant.

dynamic foo = account;
foo.Reference = "124ds4EE2s";
6
Rob Sedgwick

J'ai utilisé la méthode de réflexion de Richard, mais j'ai élaboré la méthode set pour gérer d'autres types utilisés tels que les chaînes et les valeurs nulles.

public object this[string propertyName]
{
    get
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        return property.GetValue(this, null);
    }
    set
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        Type propType = property.PropertyType;
        if (value == null)
        {
            if (propType.IsValueType && Nullable.GetUnderlyingType(propType) == null)
            {
                throw new InvalidCastException();
            }
            else
            {
                property.SetValue(this, null, null);
            }
        }
        else if (value.GetType() == propType)
        {
            property.SetValue(this, value, null);
        }
        else
        {
            TypeConverter typeConverter = TypeDescriptor.GetConverter(propType);
            object propValue = typeConverter.ConvertFromString(value.ToString());
            property.SetValue(this, propValue, null);
        }
    }
}

La fonction SetValue () générera une erreur si la conversion ne fonctionne pas.

6
Ken Mc

S'il s'agit de vos propres objets, vous pouvez fournir un indexeur pour accéder aux champs. Je ne le recommande pas vraiment mais cela permettrait ce que vous voulez.

public object this[string propertyName]
{
    get
    {
        if(propertyName == "Reference")
            return this.Reference;
        else
            return null;
    }
    set
    {
        if(propertyName == "Reference")
            this.Reference = value;
        else
            // do error case here            
    }
}

Notez que vous perdez la sécurité du type lors de cette opération.

6
Stephan

Personnellement, je préfère travailler avec des méthodes d'extension alors voici mon code:

public static class ReflectionExtensions
{
    public static void SetPropertyValue(this object Target,
        string PropertyName,
        object NewValue)
    {
        if (Target == null) return; //or throw exception

        System.Reflection.PropertyInfo prop = Target.GetType().GetProperty(PropertyName);

        if (prop == null) return; //or throw exception

        object value = prop.GetValue(Target, null);

        prop.SetValue(Target, NewValue, null);
    }
}
2
Stefan Michev

Je suis d'accord avec les affiches précédentes que vous devez probablement utiliser les propriétés. La réflexion est très lente par rapport à l'accès direct à la propriété.

D'un autre côté, si vous devez conserver une liste de propriétés définies par l'utilisateur, vous ne pouvez pas utiliser les propriétés C #. Vous devez prétendre que vous êtes un Dictionary, ou vous devez exposer une propriété qui se comporte comme un Dictionary. Voici un exemple de la façon dont vous pourriez faire en sorte que la classe Account prenne en charge les propriétés définies par l'utilisateur:

public class Account
{
    Dictionary<string, object> properties;
    public object this[string propertyName]
    {
        get
        {
            if (properties.ContainsKey[propertyName])
                return properties[propertyName];
            else
                return null;
        }
        set
        {
            properties[propertyName] = value;
        }
    }
}
1
Paul Williams

Vous devez utiliser Reflection:

PropertyInfo property = typeof(Account).GetProperty("Reference");

property.SetValue(myAccount, "...", null);

Notez que ce sera très lent.

1
SLaks