私はこのシナリオのダミーです。
これらが何であるかをGoogleで読み取ろうとしましたが、わかりません。誰かがそれらが何であるか、なぜそれらが役立つのかを簡単に説明できますか?
編集: .Net の LINQ 機能について話しています。
私はこのシナリオのダミーです。
これらが何であるかをGoogleで読み取ろうとしましたが、わかりません。誰かがそれらが何であるか、なぜそれらが役立つのかを簡単に説明できますか?
編集: .Net の LINQ 機能について話しています。
The best explanation about expression trees I ever read is this article by Charlie Calvert.
To sum it up;
An expression tree represents what you want to do, not how you want to do it.
Consider the following very simple lambda expression:
Func<int, int, int> function = (a, b) => a + b;This statement consists of three sections:
- A declaration:
Func<int, int, int> function- An equals operator:
=- A lambda expression:
(a, b) => a + b;The variable
functionpoints at raw executable code that knows how to add two numbers.
This is the most important difference between delegates and expressions. You call function (a Func<int, int, int>) without ever knowing what it will do with the two integers you passed. It takes two and returns one, that's the most your code can know.
In the previous section, you saw how to declare a variable that points at raw executable code. Expression trees are not executable code, they are a form of data structure.
Now, unlike delegates, your code can know what an expression tree is meant to do.
LINQ provides a simple syntax for translating code into a data structure called an expression tree. The first step is to add a using statement to introduce the
Linq.Expressionsnamespace:
using System.Linq.Expressions;Now we can create an expression tree:
Expression<Func<int, int, int>> expression = (a, b) => a + b;The identical lambda expression shown in the previous example is converted into an expression tree declared to be of type
Expression<T>. The identifierexpressionis not executable code; it is a data structure called an expression tree.
That means you can't just invoke an expression tree like you could invoke a delegate, but you can analyze it. So what can your code understand by analyzing the variable expression?
// `expression.NodeType` returns NodeType.Lambda.
// `expression.Type` returns Func<int, int, int>.
// `expression.ReturnType` returns Int32.
var body = expression.Body;
// `body.NodeType` returns ExpressionType.Add.
// `body.Type` returns System.Int32.
var parameters = expression.Parameters;
// `parameters.Count` returns 2.
var firstParam = parameters[0];
// `firstParam.Name` returns "a".
// `firstParam.Type` returns System.Int32.
var secondParam = parameters[1].
// `secondParam.Name` returns "b".
// `secondParam.Type` returns System.Int32.
Here we see that there is a great deal of information we can get from an expression.
But why would we need that?
You have learned that an expression tree is a data structure that represents executable code. But so far we have not answered the central question of why one would want to make such a conversion. This is the question we asked at the beginning of this post, and it is now time to answer it.
A LINQ to SQL query is not executed inside your C# program. Instead, it is translated into SQL, sent across a wire, and executed on a database server. In other words, the following code is never actually executed inside your program:
var query = from c in db.Customers where c.City == "Nantes" select new { c.City, c.CompanyName };It is first translated into the following SQL statement and then executed on a server:
SELECT [t0].[City], [t0].[CompanyName] FROM [dbo].[Customers] AS [t0] WHERE [t0].[City] = @p0The code found in a query expression has to be translated into a SQL query that can be sent to another process as a string. In this case that process happens to be a SQL server database. It is obviously going to be much easier to translate a data structure such as an expression tree into SQL than it is to translate raw IL or executable code into SQL. To exaggerate the difficulty of the problem somewhat, just imagine trying to translate a series of zeros and ones into SQL!
When it is time to translate your query expression into SQL, the expression tree representing your query is taken apart and analyzed, just as we took apart our simple lambda expression tree in the previous section. Granted, the algorithm for parsing the LINQ to SQL expression tree is much more sophisticated than the one we used, but the principle is the same. Once it has analyzed the parts of the expression tree, then LINQ mulls them over and decides the best way to write a SQL statement that will return the requested data.
Expression trees were created in order to make the task of converting code such as a query expression into a string that can be passed to some other process and executed there. It is that simple. There is no great mystery here, no magic wand that needs to be waved. One simply takes code, converts it into data, and then analyzes the data to find the constituent parts that will be translated into a string that can be passed to another process.
Because the query comes to the compiler encapsulated in such an abstract data structure, the compiler is free to interpret it in almost any way it wants. It is not forced to execute the query in a particular order, or in a particular way. Instead, it can analyze the expression tree, discover what you want done, and then decide how to do it. At least in theory, it has the freedom to consider any number of factors, such as the current network traffic, the load on the database, the current results sets it has available, etc. In practice LINQ to SQL does not consider all these factors, but it is free in theory to do pretty much what it wants. Furthermore, one could pass this expression tree to some custom code you write by hand which could analyze it and translate it into something very different from what is produced by LINQ to SQL.
Once again, we see that the expression trees allow us to represent (express?) what we want to do. And we use translators that decide how our expressions are getting used.
式ツリーは、実行可能コードをデータに変換するメカニズムです。式ツリーを使用して、プログラムを表すデータ構造を作成できます。
Expression<T>C#では、クラスを使用して、ラムダ式によって生成された式ツリーを操作できます。
従来のプログラムでは、次のようなコードを記述します。
double hypotenuse = Math.Sqrt(a*a + b*b);
このコードにより、コンパイラーは割り当てを生成します。それだけです。ほとんどの場合、気にするのはそれだけです。
従来のコードでは、アプリケーションはさかのぼって戻って、呼び出しhypotenuseを実行することによって生成されたものであるかどうかを確認することはできません。Math.Sqrt()この情報は、含まれているものの一部ではありません。
ここで、次のようなラムダ式について考えてみます。
Func<int, int, double> hypotenuse = (a, b) => Math.Sqrt(a*a + b*b);
これは以前とは少し異なります。これhypotenuseは、実際には実行可能コードのブロックへの参照です。あなたが電話する場合
hypotenuse(3, 4);
返される値を取得します5。
式ツリーを使用して、生成された実行可能コードのブロックを調べることができます。代わりにこれを試してください:
Expression<Func<int, int, int>> addTwoNumbersExpression = (x, y) => x + y;
BinaryExpression body = (BinaryExpression) addTwoNumbersExpression.Body;
Console.WriteLine(body);
これにより、次のものが生成されます。
(x + y)
式ツリーを使用すると、より高度な手法と操作が可能になります。
式ツリーは、算術式やブール式など、式のメモリ内表現です。たとえば、算術式について考えてみます。
a + b*2
*は+よりも演算子の優先順位が高いため、式ツリーは次のように作成されます。
[+]
/ \
a [*]
/ \
b 2
このツリーがあれば、aとbの任意の値を評価できます。さらに、たとえば式を導出するために、それを他の式ツリーに変換できます。
式ツリーを実装するときは、基本クラスの式を作成することをお勧めします 。それから派生して、クラスBinaryExpressionは、+や*などのすべてのバイナリ式に使用されます。次に、 VariableReferenceExpressionを参照変数(aやbなど)に導入し、別のクラスConstantExpression(例の2 )を導入できます。
式ツリーは、多くの場合、(ユーザーから直接、またはファイルから)入力を解析した結果として構築されます。式ツリーを評価するには、Visitorパターンを使用することをお勧めします。
簡単な答え:同じ種類のLINQクエリを記述して、任意のデータソースにポイントできるのは素晴らしいことです。それなしでは「統合言語クエリ」を作成することはできません。
長い答え:ご存知かもしれませんが、ソースコードをコンパイルすると、ある言語から別の言語に変換されます。通常、高級言語(C#)から(IL)の低いレバーまで。
これを行うには、基本的に2つの方法があります。
後者は、「コンパイラ」として知られているすべてのプログラムが行うことです。
解析ツリーができたら、それを他の言語に簡単に翻訳できます。これが、式ツリーでできることです。コードはデータとして保存されるため、やりたいことは何でもできますが、おそらく他の言語に翻訳したいと思うでしょう。
現在、LINQ to SQLでは、式ツリーがSQLコマンドに変換されてから、ネットワーク経由でデータベースサーバーに送信されます。私の知る限り、彼らはコードを翻訳するときに本当に派手なことは何もしませんが、できます。たとえば、クエリプロバイダーは、ネットワークの状態に応じて異なるSQLコードを作成できます。
IIUC、式ツリーは抽象構文ツリーに似ていますが、式は通常単一の値を生成しますが、AST はプログラム全体 (クラス、パッケージ、関数、ステートメントなど) を表すことができます。
とにかく、式 (2 + 3) * 5 の場合、ツリーは次のようになります。
*
/ \
+ 5
/ \
2 3
各ノードを再帰的に (ボトムアップで) 評価して、ルート ノードの値、つまり式の値を取得します。
もちろん、単項 (否定) または 3 項 (if-then-else) 演算子も使用できます。また、式言語で許可されている場合は、関数 (n-ary、つまり任意の数の op) も使用できます。
型の評価と型制御の実行は、類似したツリーに対して行われます。
あなたが参照している式ツリーは式評価ツリーですか?
はいの場合、それはパーサーによって構築されたツリーです。パーサーはレクサー/トークナイザーを使用して、プログラムからトークンを識別しました。パーサーは、トークンからバイナリ ツリーを構築します。
詳しい説明はこちら