私はファイルに格納されているいくつかの式を持つシステムを構築する必要があります。これらの式はプログラムに読み込まれ、linq式にコンパイルされ、多数のオブジェクトの関数として評価されます。ただし、これらの式にはコード内の一部の関数への参照が含まれています(つまり、基本的なC#演算子では作成されません)。
私はSystem.Linq.DynamicからDynamicExpressionを使用しようとしており、コードからの関数が認識されない点を除いてほとんど機能します。基本的に、私はこのコードがある場合:
public class GeoObj
{
public int layer;
public Polygon poly;
}
class Program
{
public static bool ComplicatedMethod(GeoObj x, GeoObj y)
{
bool result = // some quite complicated computation on x and y, say a polygon intersection test
return result;
}
static void Main(string[] args)
{
GeoObj o1 = new GeoObj(); // here set o1 fields
GeoObj o2 = new GeoObj(); // here set o2 fields
string sExpr = @"(x.layer == y.layer) && (ComplicatedMethod(x,y))";
var xparam = Expression.Parameter(typeof(GeoObj), "x");
var yparam = Expression.Parameter(typeof(GeoObj), "y");
Expression<Func<GeoObj, GeoObj, bool>> expr = (Expression<Func<GeoObj, GeoObj, bool>>)System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { xparam, yparam }, typeof(bool), sExpr);
var del2 = expr.Compile();
Console.WriteLine(del2(o1, o2));
}
}
(もちろん、最終版では、sExprはテキストファイルから読み込まれますが、これは一例です)。
コードは、 "CompleicatedFunction"が不明であることを示すParseLambdaで例外をスローします。 ParseLambdaはどのアセンブリで使用されているのかわからないので、 "ComplicatedFunction"についてParseLambdaがどのように知りませんかを見ることができます。
その文字列を実際の式にコンパイルする方法はありますか?私のコードで次のようにハードコードされた式を使って式を作成した場合:
Expression<Func<GeoObj, GeoObj, bool>> expr = (x, y) => (x.layer == y.layer) && ComplicatedFunction(x,y));
私の意図どおりに動作しますが、可能な表現はすべて期待できません。
それが不可能な場合は、文字列式をパースツリーに解析し、そのツリーから式を作成することをお勧めします。使用される機能(「ComplicatedFunction」など)はほんのわずかなので、実行可能に見えます。そのような式から構文木を作成する最良の方法/ベストコードは何ですか?シンボルがわからないとき(例外的に "ComplicatedFunction"のような)例外がスローされないようなものが必要で、ツリーを解析して式を構築するのが簡単になります。
ありがとうございました。
--------------------更新----------------------------- ----
@ pil0tの答えは、これを解決するための正しい道に私を置きました。基本的には、Dynamic Linqのオリジナルのソースファイルをダウンロードし、2つの変更を加えなければなりません。
探す:
static readonly Type[] predefinedTypes = {
'readonly'キーワードを削除します(変更できるようになりました)。
次に、次の関数をExpressionParserクラスに追加します。
public static void AddPredefinedType(Type type)
{
predefinedTypes = predefinedTypes.Union(new[] { type }).ToArray();
keywords = ExpressionParser.CreateKeywords();
}
次のような新しい関数を使うことができます:
using System.Linq.Dynamic;
...
ExpressionParser.AddPredefinedType(typeof(GeoObj));
しかし、表現の構文に関する1つの注意。新しく追加された関数は、 'GeoObj'クラス(または使用するクラス)のメソッドにアクセスすることができます。つまり、次のように式を変更する必要があります。
class GeoObj {
public bool field1;
public bool method1() { /* return something */ }
public static ComplicatedMethod(GeoObj o1, GeoObj o2) { ... }
}
...
string myExpr = @"x.field1 && y.method1() && GeoObj.ComplicatedMethod(x,y)";
つまり、@ Shlomoのように、使用する必要があるすべての関数をクラス内で移動する必要があります。 "GeoObj.ComplicatedMethod(x、y)"の代わりに "ComplicatedMethod(x、y)"を使用するか、x.Method1()の代わりにMethod1(x)のようなものを使用するなど、これらは今のところ審美的な好みである。また、使用できるメソッドの数が少ないため、式を関数呼び出し式からメソッド呼び出しに書き換えることができます。
私に正しい方向を向けることに感謝します。
私は元のソースにパッチを当てて
public class Foo
{
...
public static void AddPredefinedType(Type type)
{
ExpressionParser.predefinedTypes = ExpressionParser.predefinedTypes.Union(new[] { type }).ToArray();
ExpressionParser.CreateKeywords();
}
}
その後、あなたは
Foo.AddPredefinedType(typeof(Program));
プログラムのすべてのメソッドが利用可能になります
私はDynamicExpression
ものであなたを本当に助けることはできません。式を作成する方法をお探しの場合は、これが始めるのに役立ちます。
あなたのクラスGeoObj
が次のようになっていると仮定します:
class GeoObj
{
public static bool ComplicatedFunction(GeoObj a, GeoObj b)
{
return false;
}
public object layer { get; set; }
}
あなたの例のものと似た表現を作る方法は次のとおりです:
Expression<Func<GeoObj, GeoObj, bool>> expr = (x, y) => (x.layer == y.layer) && GeoObj.ComplicatedFunction(x,y);
var complicatedFunctionMethodInfo = typeof (GeoObj).GetMethod("ComplicatedFunction");
var paramX = Expression.Parameter(typeof (GeoObj), "x");
var paramY = Expression.Parameter(typeof (GeoObj), "y");
var expr2 = Expression.Lambda<Func<GeoObj, GeoObj, bool>>(
Expression.AndAlso
(
Expression.Equal
(
Expression.Property(paramX, "layer"),
Expression.Property(paramY, "layer")
),
Expression.Call(complicatedFunctionMethodInfo, paramX, paramY)
),
paramX,
paramY
);
expr
とexpr2
は機能的に同等です。