Obtenir la valeur de la propriété dynamique c # via une chaîne


182

Je voudrais accéder à la valeur d'une dynamicpropriété c # avec une chaîne:

dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };

Comment puis-je obtenir la valeur de d.value2 ("random") si je n'ai que "value2" comme chaîne? En javascript, je pourrais faire d ["value2"] pour accéder à la valeur ("random"), mais je ne sais pas comment faire cela avec c # et la réflexion. Le plus proche que je suis venu est celui-ci:

d.GetType().GetProperty("value2") ... mais je ne sais pas comment en tirer la valeur réelle.

Comme toujours, merci pour votre aide!


26
Notez que ce n'est pas le but de «dynamique» et que ce scénario ne fonctionne pas mieux avec «dynamique» qu'avec «objet». "dynamic" permet d'accéder aux propriétés lorsque le nom de la propriété est connu au moment de la compilation mais que le type ne l'est pas. Puisque vous ne connaissez ni le nom ni le type au moment de la compilation, dynamic ne vous aidera pas.
Eric Lippert


3
@EricLippert Je sais que cette question est ancienne, mais juste pour faire un commentaire au cas où quelqu'un la verrait dans le futur. Dans certains cas, vous ne pouvez pas choisir d'utiliser dynamique ou objet (par exemple lors de l'utilisation de l'analyseur JSON) et vous pouvez toujours vouloir obtenir les propriétés d'une chaîne (à partir d'un fichier de configuration par exemple), donc cette utilisation n'est pas si inhabituelle comme on pourrait le penser au départ.
Pedrom

Réponses:


217

Une fois que vous avez votre PropertyInfo(de GetProperty), vous devez appeler GetValueet transmettre l'instance dont vous souhaitez obtenir la valeur. Dans ton cas:

d.GetType().GetProperty("value2").GetValue(d, null);

4
Je reçois un 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}dans la fenêtre de surveillance avec ça ..?
TimDog

6
Think GetValue a besoin d'un paramètre supplémentaire - egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer

3
Cela fonctionnera-t-il sur un véritable ExpandoObject dynamique plutôt que sur un type anonyme? Puisque new {}crée un vrai type anonyme avec des propriétés définies, appeler GetType / GetProperty a du sens, mais qu'en est-il de ExpandoObject, qui si vous appelez GetType, vous obtiendrez un type qui a les propriétés de ExpandoObject, mais pas nécessairement ses propriétés dynamiques.
Triynko

16
-1. Cela ne fonctionne qu'avec des objets .NET simples qui ont été convertis en dynamiques. Il ne fonctionnera avec aucun objet dynamique personnalisé tel que Expando ou ViewBag utilisé ASP.NET MVC
Philipp Munin

8
voici

39
public static object GetProperty(object target, string name)
{
    var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
    return site.Target(site, target);
}

Ajoutez une référence à Microsoft.CSharp. Fonctionne également pour les types dynamiques et les propriétés et champs privés.

Edit : Bien que cette approche fonctionne, il existe une méthode presque 20 fois plus rapide de l' assemblage Microsoft.VisualBasic.dll :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}

2
Je voulais juste mentionner que la version VisualBasic n'est pas équivalente à votre version originale 'GetProperty' (le GetProperty appelle en fait le GetMember dynamique, qui fonctionne même sur les objets Python dans IronPython).
Trevor Sundberg

quelle serait la cible de l'objet?
Demodave

@Demodave L'objet sur lequel vous souhaitez invoquer la propriété ( ddans la question).
IllidanS4 veut que Monica revienne le

➕1 cela a fonctionné pour les propriétés privées quand FastMember et HyperDescriptor ne le feraient pas
Chris Marisic

@ IllidanS4 lorsque vous avez comparé le CallSitecode au CallByNamecode, avez-vous comparé les deux lors de la mise en cache de l' CallSiteinstance? Je soupçonne que le coût de votre première méthode est presque purement l'activation du Binderet CallSite, pas l'invocation deTarget()
Chris Marisic

24

Dynamitey est une .net stdbibliothèque open source , qui vous permet de l'appeler comme le dynamicmot - clé, mais en utilisant la chaîne a pour le nom de la propriété plutôt que le compilateur le faisant pour vous, et cela finit par être égal à la réflexion speedwise (ce qui n'est pas aussi rapide comme utilisant le mot-clé dynamic, mais cela est dû à la surcharge supplémentaire de la mise en cache dynamique, où le compilateur met en cache de manière statique).

Dynamic.InvokeGet(d,"value2");

11

La méthode la plus simple pour obtenir à la fois a setteret a getterpour une propriété qui fonctionne pour tout type, y compris dynamicet ExpandoObjectconsiste à utiliserFastMember ce qui se trouve être également la méthode la plus rapide (elle utilise Emit).

Vous pouvez soit obtenir un TypeAccessorbasé sur un type donné, soit un ObjectAccessorbasé sur une instance d'un type donné.

Exemple:

var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");

var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");

dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");

var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");

typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");

typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");

8

La plupart du temps, lorsque vous demandez un objet dynamique, vous obtenez un ExpandoObject (pas dans l'exemple anonyme mais statique de la question ci-dessus, mais vous mentionnez JavaScript et mon analyseur JSON choisi JsonFx, pour sa part, génère ExpandoObjects).

Si votre dynamique est en fait un ExpandoObject, vous pouvez éviter la réflexion en le convertissant en IDictionary, comme décrit à http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Une fois que vous avez converti en IDictionary, vous avez accès à des méthodes utiles telles que .Item et .ContainsKey


Malheureusement, avoir à effectuer un cast en IDictionary et à utiliser TryGetValue par exemple, entraîne le retour d'un ancien objet. Vous ne pouvez pas tirer parti des opérateurs implicites à ce stade, car ils ne sont pris en compte qu'au moment de la compilation. Par exemple, si j'avais une classe Int64Proxy avec une conversion implicite en Int64 ?, alors Int64? i = data.value; //data is ExpandoObjectrechercherait et appellerait automatiquement l'opérateur implicite. D'un autre côté, si je devais utiliser IDictionary pour tester si le champ "valeur" existe, je récupérerais un objet qui ne sera pas converti sans erreur en Int64 ?.
Triynko

5

GetProperty / GetValue ne fonctionne pas pour les données Json, il génère toujours une exception null, cependant, vous pouvez essayer cette approche:

Sérialisez votre objet à l'aide de JsonConvert:

var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));

Ensuite, accédez-y directement en le renvoyant en chaîne:

var pn = (string)z["DynamicFieldName"];

Cela peut fonctionner directement en appliquant le Convert.ToString (request) ["DynamicFieldName"], mais je n'ai pas testé.


2
Cette méthode génère l'erreur: erreur CS0021: Impossible d'appliquer l'indexation avec [] à une expression de type «objet». Utilisez new JavaScriptSerializer().Deserialize<object>(json);pour accéder aux "propriétés" de la manière que vous avez suggérée
Kris Kilton

4

d.GetType (). GetProperty ("valeur2")

renvoie un objet PropertyInfo.

Alors fais

propertyInfo.GetValue(d)

2
merci, c'était la bonne réponse, mais comme mentionné ci-dessus, les GetValue(d)besoins doivent êtreGetValue(d,null)
TimDog

4

C'est ainsi que j'ai obtenu la valeur d'une valeur de propriété d'un dinamic:

    public dynamic Post(dynamic value)
    {            
        try
        {
            if (value != null)
            {
                var valorCampos = "";

                foreach (Newtonsoft.Json.Linq.JProperty item in value)
                {
                    if (item.Name == "valorCampo")//property name
                        valorCampos = item.Value.ToString();
                }                                           

            }
        }
        catch (Exception ex)
        {

        }


    }

1

Pour obtenir les propriétés de la documentation dynamique lors des .GetType()retours null, essayez ceci:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;

0

Dans .Net core 3.1, vous pouvez essayer comme ceci

d?.value2 , d?.value3

0

Similaire à la réponse acceptée, vous pouvez également essayer à la GetFieldplace de GetProperty.

d.GetType().GetField("value2").GetValue(d);

Selon la façon dont le réel a Typeété implémenté, cela peut fonctionner lorsque GetProperty () ne le fait pas et peut même être plus rapide.


Différence FYI entre la propriété et le champ en C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.