web-dev-qa-db-fra.com

Comment définir une propriété d'un objet dynamique C # 4 lorsque vous avez le nom dans une autre variable

Je recherche un moyen de modifier les propriétés d'un dynamic objet C # 4.0 avec le nom de la propriété connu uniquement au moment de l'exécution.

Existe-t-il un moyen de faire quelque chose comme (ExpandoObject est juste utilisé comme exemple, cela pourrait être n'importe quelle classe qui implémente IDynamicMetaObjectProvider):

string key = "TestKey";
dynamic e = new ExpandoObject();
e[key] = "value";

Ce qui équivaudrait à:

dynamic e = new ExpandoObject();
e.TestKey = "value";

Ou est-ce la seule façon d'avancer la réflexion?

43
Kieran Benton

Pas très facilement, non. La réflexion ne fonctionne pas, car elle suppose un modèle de type régulier, qui est pas la gamme complète de dynamic. Si vous ne faites que parler à des objets normaux, utilisez simplement la réflexion ici. Sinon, je pense que vous voudrez peut-être effectuer une rétro-ingénierie du code que le compilateur émet pour une affectation de base, et l'ajuster pour avoir un nom de membre flexible. Je serai honnête, cependant: ce n'est pas une option attrayante; un simple:

dynamic foo = ...
foo.Bar = "abc";

se traduit par:

if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
}
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc");

Si vous voulez une approche qui fonctionne à la fois pour les objets dynamiques et non dynamiques: FastMember est pratique pour cela, et fonctionne au niveau du type ou de l'objet:

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]);

disponible sur Nuget et fortement optimisé pour les scénarios dynamiques et non dynamiques.

16
Marc Gravell

Paul Sasik a répondu à une question similaire sur C # 4.0 Dynamic vs Expando ... où vont-ils?

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        var p = expando as IDictionary<String, object>;
        p["A"] = "New val 1";
        p["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}
60
Jonas Follesø

Pour ajouter à la réponse de Jonas, vous n'avez pas besoin de créer une nouvelle var p. Utilisez plutôt cette approche:

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        ((IDictionary<String, object>)expando)["A"] = "New val 1";
        ((IDictionary<String, object>)expando)["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}
9
Jazimov

Mon framework open source Dynamitey a des méthodes d'invocation basées sur des noms de chaîne en utilisant le DLR. Il fait le travail de mise en cache des sites de liaison et le rationalise en un seul appel de méthode. elle s'exécute également plus rapidement que la réflexion sur des objets non dynamiques.

Dynamic.InvokeSet(e, "TestKey", "value");
5
jbtule

membre rapide peut convenir - il semble qu'il génère l'IL à la volée, mais le met en cache donc c'est vraiment rapide après la première utilisation.

0
Marc Clifton