나는이 시나리오에서 더미입니다.
나는 이것이 무엇인지 Google에서 읽으려고했지만 이해하지 못합니다. 누군가가 그들이 무엇이며 왜 유용한 지에 대한 간단한 설명을 줄 수 있습니까?
편집 : .Net의 LINQ 기능에 대해 이야기하고 있습니다.
나는이 시나리오에서 더미입니다.
나는 이것이 무엇인지 Google에서 읽으려고했지만 이해하지 못합니다. 누군가가 그들이 무엇이며 왜 유용한 지에 대한 간단한 설명을 줄 수 있습니까?
편집 : .Net의 LINQ 기능에 대해 이야기하고 있습니다.
답변:
내가 읽은 표현 트리에 대한 가장 좋은 설명은 Charlie Calvert 의이 기사 입니다.
그것을 요 약하기;
식 트리를 나타내는 것을 당신이하지,하고 싶은 방법 당신이 그것을 할 싶어.
다음과 같은 매우 간단한 람다 식을 고려하십시오.
Func<int, int, int> function = (a, b) => a + b;
이 명령문은 세 섹션으로 구성됩니다.
- 선언 :
Func<int, int, int> function
- 같음 연산자 :
=
- 람다 식 :
(a, b) => a + b;
변수 는 두 숫자를 더하는 방법을 알고
function
있는 원시 실행 코드를 가리 킵니다 .
이것이 대표자와 표현의 가장 중요한 차이점입니다. 전달한 두 정수로 무엇을할지 알지 못한 채 function
(a Func<int, int, int>
)를 호출 합니다. 2 개가 필요하고 1 개가 반환됩니다. 이는 코드가 알 수있는 최대 값입니다.
이전 섹션에서 원시 실행 코드를 가리키는 변수를 선언하는 방법을 보았습니다. 표현식 트리는 실행 가능한 코드가 아니며 데이터 구조의 한 형태입니다.
이제 델리게이트와 달리 코드 에서 표현식 트리의 기능을 알 수 있습니다 .
LINQ는 코드를 식 트리라는 데이터 구조로 변환하기위한 간단한 구문을 제공합니다. 첫 번째 단계는
Linq.Expressions
네임 스페이스 를 도입하는 using 문을 추가하는 것입니다 .
using System.Linq.Expressions;
이제 표현식 트리를 만들 수 있습니다.
Expression<Func<int, int, int>> expression = (a, b) => a + b;
이전 예제에 표시된 동일한 람다식이 형식으로 선언 된 식 트리로 변환됩니다
Expression<T>
. 식별자expression
는 실행 가능한 코드 가 아닙니다 . 표현식 트리라고하는 데이터 구조입니다.
즉, 델리게이트를 호출 할 수있는 것처럼 표현식 트리를 호출 할 수는 없지만 분석 할 수 있습니다. 그렇다면 코드는 변수를 분석하여 무엇을 이해할 수 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.
여기서 우리는 표현식에서 얻을 수있는 많은 정보가 있음을 알 수 있습니다.
하지만 왜 우리가 필요합니까?
식 트리가 실행 코드를 나타내는 데이터 구조라는 것을 배웠습니다. 그러나 지금까지 우리는 왜 그러한 전환을 원하는지에 대한 핵심 질문에 답하지 않았습니다. 이것은이 게시물의 시작 부분에서 우리가 물었던 질문이며 이제 대답 할 시간입니다.
LINQ to SQL 쿼리는 C # 프로그램 내에서 실행되지 않습니다. 대신 SQL로 변환되어 유선을 통해 전송되고 데이터베이스 서버에서 실행됩니다. 즉, 다음 코드는 실제로 프로그램 내에서 실행되지 않습니다.
var query = from c in db.Customers where c.City == "Nantes" select new { c.City, c.CompanyName };
먼저 다음 SQL 문으로 변환 된 다음 서버에서 실행됩니다.
SELECT [t0].[City], [t0].[CompanyName] FROM [dbo].[Customers] AS [t0] WHERE [t0].[City] = @p0
쿼리 식에서 찾은 코드는 다른 프로세스에 문자열로 보낼 수있는 SQL 쿼리로 변환되어야합니다. 이 경우 해당 프로세스는 SQL 서버 데이터베이스가됩니다. 원시 IL 또는 실행 코드를 SQL로 변환하는 것보다 표현식 트리와 같은 데이터 구조를 SQL로 변환하는 것이 훨씬 쉬울 것입니다. 문제의 난이도를 다소 과장하려면 일련의 0과 1을 SQL로 변환하는 것을 상상해보십시오!
쿼리 식을 SQL로 변환 할 때가되면 이전 섹션에서 간단한 람다 식 트리를 분리 한 것처럼 쿼리를 나타내는 식 트리를 분리하고 분석합니다. 물론 LINQ to SQL 식 트리를 구문 분석하는 알고리즘은 우리가 사용한 것보다 훨씬 정교하지만 원칙은 동일합니다. 식 트리의 일부를 분석 한 후 LINQ는이를 검토하고 요청 된 데이터를 반환 할 SQL 문을 작성하는 가장 좋은 방법을 결정합니다.
쿼리 식과 같은 코드를 다른 프로세스에 전달하여 실행할 수있는 문자열로 변환하는 작업을 만들기 위해 식 트리가 만들어졌습니다. 그렇게 간단합니다. 여기에는 큰 수수께끼가 없으며 흔들어야 할 마술 지팡이도 없습니다. 코드를 가져 와서 데이터로 변환 한 다음 데이터를 분석하여 다른 프로세스로 전달할 수있는 문자열로 변환 될 구성 요소를 찾습니다.
쿼리는 이러한 추상 데이터 구조로 캡슐화 된 컴파일러에 전달되기 때문에 컴파일러는 원하는 거의 모든 방식으로 자유롭게 해석 할 수 있습니다. 특정 순서 또는 특정 방식으로 쿼리를 실행하도록 강요되지 않습니다. 대신 표현식 트리를 분석하고 원하는 작업을 찾은 다음 수행 방법을 결정할 수 있습니다. 적어도 이론적으로는 현재 네트워크 트래픽, 데이터베이스의 부하, 사용 가능한 현재 결과 집합 등과 같은 여러 요소를 자유롭게 고려할 수 있습니다. 실제로 LINQ to SQL은 이러한 요소를 모두 고려하지 않습니다. 하지만 이론적으로는 원하는 것을 거의 할 수 있습니다. 또한이 식 트리를 직접 작성한 사용자 지정 코드에 전달할 수 있으며이를 분석하여 LINQ to SQL에서 생성 된 것과 매우 다른 것으로 변환 할 수 있습니다.
다시 한 번, 표현 트리를 통해 우리 가 원하는 것을 표현 (표현?) 할 수 있음을 알 수 있습니다. 그리고 우리 는 표현이 어떻게 사용 되는지 를 결정 하는 번역자를 사용합니다.
표현식 트리는 실행 코드를 데이터로 변환하는 메커니즘입니다. 표현식 트리를 사용하여 프로그램을 나타내는 데이터 구조를 생성 할 수 있습니다.
C #에서는 Expression<T>
클래스 를 사용하여 람다 식으로 생성 된 식 트리로 작업 할 수 있습니다 .
전통적인 프로그램에서는 다음과 같은 코드를 작성합니다.
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의 모든 값에 대해 평가할 수 있습니다. 또한 표현식을 파생하기 위해 다른 표현식 트리로 변환 할 수 있습니다.
식 트리를 구현할 때 기본 클래스 Expression 을 만드는 것이 좋습니다 . 그로부터 파생 된 BinaryExpression 클래스 는 + 및 *와 같은 모든 이진 표현식에 사용됩니다. 그런 다음 변수 를 참조하기 위해 VariableReferenceExpression (예 : a 및 b)과 다른 클래스 ConstantExpression (예제에서 2)을 도입 할 수 있습니다.
표현식 트리는 대부분의 경우 입력을 구문 분석 한 결과 (사용자가 직접 또는 파일에서) 빌드됩니다. 표현식 트리를 평가하기 위해 방문자 패턴 을 사용하는 것이 좋습니다 .
짧은 대답 : 같은 종류의 LINQ 쿼리를 작성하고 모든 데이터 소스를 가리킬 수 있다는 것이 좋습니다. 그것 없이는 "언어 통합"쿼리를 가질 수 없습니다.
긴 답변 : 아시다시피 소스 코드를 컴파일 할 때 한 언어에서 다른 언어로 변환하게됩니다. 일반적으로 높은 수준의 언어 (C #)에서 낮은 레버 (IL)로.
기본적으로 두 가지 방법이 있습니다.
후자는 '컴파일러'로 알려진 모든 프로그램이하는 일입니다.
파스 트리가 있으면 다른 언어로 쉽게 번역 할 수 있으며 이것이 바로 표현식 트리가 우리가 할 수있는 일입니다. 코드가 데이터로 저장되기 때문에 원하는 것은 무엇이든 할 수 있지만 아마도 다른 언어로 번역하고 싶을 것입니다.
이제 LINQ to SQL에서 식 트리는 SQL 명령으로 변환 된 다음 유선을 통해 데이터베이스 서버로 전송됩니다. 내가 아는 한 코드를 번역 할 때 그들은 정말 멋진 아무것도하지 않는다하지만 그들은 할 수 있었다 . 예를 들어 쿼리 공급자는 네트워크 조건에 따라 다른 SQL 코드를 만들 수 있습니다.
IIUC, 표현식 트리는 추상 구문 트리와 유사하지만 표현식은 일반적으로 단일 값을 산출하는 반면 AST는 전체 프로그램 (클래스, 패키지, 함수, 명령문 등)을 나타낼 수 있습니다.
어쨌든 식 (2 + 3) * 5의 경우 트리는 다음과 같습니다.
*
/ \
+ 5
/ \
2 3
각 노드를 재귀 적으로 (상향식) 평가하여 루트 노드에서 값, 즉 표현식 값을 얻습니다.
물론 단항 (부정) 또는 삼항 (if-then-else) 연산자도 사용할 수 있으며, 표현식 언어에서 허용하는 경우 함수 (n 항, 즉 임의 개수의 연산)를 사용할 수 있습니다.
유형 평가 및 유형 제어 수행은 유사한 트리에서 수행됩니다.
DLR
식 트리는 DLR (동적 언어 런타임)을 지원하기 위해 C #에 추가되었습니다. DLR은 변수를 선언하는 "var"메소드를 제공하는 역할도합니다. ( var objA = new Tree();
)
기본적으로 Microsoft는 LISP, SmallTalk, Javascript 등과 같은 동적 언어에 대한 CLR을 공개하고자했습니다.이를 위해 그들은 즉시 식을 구문 분석하고 평가할 수 있어야했습니다. DLR이 나오기 전에는 불가능했습니다.
첫 번째 문장으로 돌아가서 식 트리는 DLR을 사용할 수있는 기능을 제공하는 C #에 추가되었습니다. 이전에 C #은 훨씬 더 정적 인 언어였습니다. 모든 변수 유형은 특정 유형으로 선언해야하고 모든 코드는 컴파일 타임에 작성해야했습니다.
데이터
표현식 트리 와 함께 사용하면 동적 코드에 대한 플러드 게이트가 열립니다.
예를 들어 부동산 사이트를 만들고 있다고 가정 해 보겠습니다. 디자인 단계에서는 적용 할 수있는 모든 필터를 알고 있습니다. 이 코드를 구현하려면 두 가지 선택이 있습니다. 각 데이터 포인트를 일련의 If-Then 검사와 비교하는 루프를 작성할 수 있습니다. 또는 동적 언어 (SQL)로 쿼리를 작성하여 검색을 수행 할 수있는 프로그램 (데이터베이스)에 전달할 수 있습니다.
이제 식 트리를 사용하여 프로그램의 코드를 즉석에서 변경하고 검색을 수행 할 수 있습니다. 특히 LINQ를 통해이 작업을 수행 할 수 있습니다.
(추가 정보 : MSDN : 방법 : 식 트리를 사용하여 동적 쿼리 작성 ).
데이터
그 이상 표현식 트리의 주요 용도는 데이터 관리입니다. 그러나 동적으로 생성 된 코드에도 사용할 수 있습니다. 따라서 동적으로 정의 된 함수 (Javascript라고도 함)를 원한다면 Expression Tree를 만들고 컴파일하고 결과를 평가할 수 있습니다.
좀 더 자세히 살펴 보 겠지만이 사이트는 훨씬 더 나은 작업을 수행합니다.
나열된 예제에는 변수 유형에 대한 일반 연산자 만들기, 수동 람다 식, 고성능 얕은 복제, 한 개체에서 다른 개체로 읽기 / 쓰기 속성 동적 복사가 포함됩니다.
요약
표현식 트리는 런타임에 컴파일되고 평가되는 코드의 표현입니다. 데이터 조작 및 동적 프로그래밍에 유용한 동적 유형을 허용합니다.
var
컴파일 타임 구문 설탕입니다-표현식 트리, DLR 또는 런타임과 관련이 없습니다. var i = 0
를 쓴 것처럼 컴파일 int i = 0
되므로 var
컴파일 타임에 알려지지 않은 유형을 나타내는 데 사용할 수 없습니다 . 식 트리는 "DLR을 지원하기위한 추가"가 아니며 LINQ를 허용하기 위해 .NET 3.5에 도입되었습니다. 반면 DLR은 .NET 4.0에 도입되어 동적 언어 (예 : IronRuby) 및 dynamic
키워드 를 허용 합니다. 식 트리는 실제로 DLR에서 interop을 제공하는 데 사용되며 그 반대는 아닙니다.