Étant donné cette classe
public class Foo
{
public string Name { get; set; }
}
Cette méthode (dans une autre classe) ...
private Func<Foo, string> Compile(string body)
{
ParameterExpression prm = Expression.Parameter(typeof(Foo), "foo");
LambdaExpression exp = DynamicExpressionParser.ParseLambda(new[] { prm }, typeof(string), body);
return (Func<Foo, string>)exp.Compile();
}
Prendra le côté droit d'une expression lambda et me rendra un délégué. Donc si on l’appelle comme ça:
Foo f = new Foo { Name = "Hamilton Academicals" };
//foo => foo.Name.Substring(0,3)
Func<Foo, string> fn = Compile("foo.Name.Substring(0,3)");
string sub = fn(f);
Alors sous aura la valeur "Ham".
Tout va bien, cependant, je voudrais créer DynamicObject de la sous-classe Foo (pour pouvoir implémenter TryGetMember afin de déterminer de manière dynamique les valeurs de propriété), je souhaite donc prendre l'expression et obtenir l'équivalent de ceci
Func<dynamic, dynamic> fn = foo => foo.Name.Substring(0,3);
J'ai essayé Expression.Dynamic
en utilisant une CallSiteBinder
personnalisée, mais cela échoue avec Aucune propriété ou champ Bar
existant dans le type Object
(lorsque j'essaie d'accéder à foo.Bar
de manière dynamique). Je suppose que cela est dû au fait que l'appel pour obtenir foo.Bar
doit être envoyé de manière dynamique (à l'aide de Expression.Dynamic
), mais cela ne fonctionnera pas pour moi car l'objectif principal est qu'un utilisateur puisse entrer une expression simple et la faire exécuter. C'est possible?
Ça marche, si ça peut t'aider:
Compilateur :
using System;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
namespace ConsoleApp4
{
public class Compiler
{
public static Func<CustomDynamic, TResult> Compile<TResult>(string body)
{
ParameterExpression prm = Expression.Parameter(typeof(CustomDynamic), typeof(CustomDynamic).Name);
LambdaExpression exp = DynamicExpressionParser.ParseLambda(new[] { prm }, typeof(TResult), body);
return (Func<CustomDynamic, TResult>)exp.Compile();
}
}
}
Objet dynamique :
using System.Collections.Generic;
using System.Dynamic;
namespace ConsoleApp4
{
public class CustomDynamic
{
ExpandoObject _values;
public CustomDynamic()
{
_values = new ExpandoObject();
}
public void AddProperty(string propertyName, object propertyValue)
{
var expandoDict = _values as IDictionary<string, object>;
if (expandoDict.ContainsKey(propertyName))
expandoDict[propertyName] = propertyValue;
else
expandoDict.Add(propertyName, propertyValue);
}
public string GetString(string propertyName)
{
var expandoDict = _values as IDictionary<string, object>;
if (expandoDict.ContainsKey(propertyName))
return (string)expandoDict[propertyName];
else
throw new KeyNotFoundException($"dynamic object did not contain property {propertyName}");
}
public int GetInt(string propertyName)
{
var expandoDict = _values as IDictionary<string, object>;
if (expandoDict.ContainsKey(propertyName))
return (int)expandoDict[propertyName];
else
throw new KeyNotFoundException($"dynamic object did not contain property {propertyName}");
}
}
}
Cas d'utilisation:
using System;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
CustomDynamic f = new CustomDynamic();
f.AddProperty("Name", "Hamiltonian Physics");
Func<CustomDynamic, string> fn = Compiler.Compile<string>("CustomDynamic.GetString(\"Name\").SubString(0, 3)");
string sub = fn(f);
Console.WriteLine(sub);
Console.ReadLine();
}
}
}
Il utilise simplement la classe ExpandoObject
. Il est regrettable que vous ayez besoin de passer par son API pour créer des objets, tels que "AddProperty" pour chaque propriété, mais heyho.
Le DynamicExpressionParser.ParseLambda
est également un problème pour les méthodes génériques. J'ai donc dû créer des accesseurs spécifiques à un type, ce qui/ n'est pas le meilleur, mais cela fonctionne.