C# Language표현식 트리


소개

표현식 트리는 트리 형태의 데이터 구조로 배열 된 표현식입니다. 트리의 각 노드는 표현식의 표현이며, 표현식은 코드입니다. Lambda 표현식의 메모리 내 표현은 표현식 트리가 될 것이고, 이는 쿼리의 실제 요소 (예 : 코드)는 포함하지만 결과는 포함하지 않습니다. 표현식 트리는 람다 표현식의 구조를 투명하고 명시 적으로 만듭니다.

통사론

  • 식 <TDelegate> 이름 = lambdaExpression;

매개 변수

매개 변수 세부
TDelegate 표현식에 사용될 델리게이트 유형
lambdaExpression 람다 식 (예 : num => num < 5 )

비고

표현식 트리 소개

우리가 온 곳

표현식 트리는 모두 런타임시 "소스 코드"를 소비합니다. 판매 주문 decimal CalculateTotalTaxDue(SalesOrder order) 에 대한 판매 세를 계산하는 방법을 고려해보십시오. .NET 프로그램 decimal taxDue = CalculateTotalTaxDue(order); 메서드를 사용하는 것은 쉽습니다. 그냥 decimal taxDue = CalculateTotalTaxDue(order); . 원격 쿼리 (SQL, XML, 원격 서버 등)의 모든 결과에 적용하려면 어떻게해야합니까? 이러한 원격 쿼리 소스는 메서드를 호출 할 수 없습니다! 전통적으로이 모든 경우에 흐름을 뒤집어 야합니다. 전체 쿼리를 만들고이를 메모리에 저장 한 다음 결과를 반복하고 각 결과에 대해 세금을 계산하십시오.

플로우 인 버전의 메모리 및 대기 시간 문제를 방지하는 방법

표현식 트리는 각 노드가 표현식을 보유하는 트리 형식의 데이터 구조입니다. 그것들은 데이타베이스 질의와 같은 프로그램 환경 밖에서 사용될 수있는 표현식에서 컴파일 된 명령어 (데이터 필터링에 사용 된 메소드와 같은)를 변환하는 데 사용됩니다.

여기서 문제는 원격 쿼리 가 메서드에 액세스 할 수 없다는 것 입니다. 대신이 메소드에 대한 명령어 를 원격 쿼리에 보낸 경우이 문제를 피할 수 있습니다. CalculateTotalTaxDue 예제에서이 정보를 보내는 것을 의미합니다.

  1. 총 세금을 저장할 변수 만들기
  2. 주문의 모든 라인을 반복하십시오.
  3. 각 라인에 대해 제품이 과세 대상인지 확인하십시오.
  4. 그러한 경우 총액에 적용 가능한 세율을 곱하고 그 금액을 총액에 더하십시오.
  5. 그렇지 않으면 아무것도하지 않습니다.

이러한 지침을 통해 원격 쿼리는 데이터를 생성하는 동안 작업을 수행 할 수 있습니다.

이를 구현하는 데에는 두 가지 문제점이 있습니다. 컴파일 된 .NET 메소드를 명령어 목록으로 변환하는 방법과 명령어를 원격 시스템에서 사용할 수있는 방식으로 포맷하는 방법은 무엇입니까?

표현식 트리가 없으면 MSIL의 첫 번째 문제 만 해결할 수 있습니다. (MSIL은 .NET 컴파일러에서 만든 어셈블러 같은 코드입니다.) MSIL 구문 분석이 가능 하지만 쉽지 않습니다. 제대로 파싱한다고하더라도 원래 프로그래머의 의도가 특정 루틴과 무엇인지 판단하기가 어려울 수 있습니다.

식 트리를 사용하면 하루를 절약 할 수 있습니다.

식 트리는 이러한 정확한 문제를 해결합니다. 그것들은 프로그램 명령을 트리 데이터 구조로 나타내며 각 노드는 하나의 명령을 나타내며 해당 명령을 실행하는 데 필요한 모든 정보를 나타냅니다. 예를 들어 MethodCallExpression 은 1) 호출 할 MethodInfo , 2) 해당 메서드에 전달할 Expression 의 목록, 3) 인스턴스 메서드, 메서드를 호출 할 Expression 합니다. "나무를 걷고"원격 쿼리에 대한 지침을 적용 할 수 있습니다.

표현 트리 만들기

표현식 트리를 만드는 가장 쉬운 방법은 람다 식을 사용하는 것입니다. 이 표현식은 일반적인 C # 메소드와 거의 같습니다. 이것이 컴파일러 마술 임을 깨닫는 것이 중요합니다. 처음에 람다 식을 만들면 컴파일러는 사용자가 할당 한 내용을 확인합니다. Delegate 유형 ( Action 또는 Func ) 인 경우 컴파일러는 람다 식을 대리자로 변환합니다. LambdaExpression (또는 강력하게 형식화 된 LambdaExpressionExpression<Action<T>> 또는 Expression<Func<T>> ) 인 경우 컴파일러는이를 LambdaExpression 으로 변환합니다. 이 부분에서 마술이 시작됩니다. 장면 뒤에서 컴파일러 는 expression tree API사용 하여 람다 식을 LambdaExpression 으로 변환합니다.

람다 식은 모든 유형의 식 트리를 만들 수 없습니다. 이러한 경우 Expressions API를 수동으로 사용하여 필요한 트리를 만들 수 있습니다. 표현식 API 이해하기 예제에서 API 를 사용하여 CalculateTotalSalesTax 표현식을 만듭니다.

참고 : 이름이 약간 혼란 스러울 수 있습니다. 람다 식 (두 단어, 소문자)은 => 표시기가있는 코드 블록을 나타냅니다. 그것은 C #에서 익명 메소드를 나타내며 Delegate 또는 Expression 으로 변환됩니다. LambdaExpression (한 단어, PascalCase)은 실행할 수있는 메서드를 나타내는 Expression API 내의 노드 형식을 참조합니다.

표현식 트리 및 LINQ

표현 트리의 가장 일반적인 용도 중 하나는 LINQ 및 데이터베이스 쿼리입니다. LINQ는 쿼리 공급자와 식 트리를 연결하여 대상 원격 쿼리에 지침을 적용합니다. 예를 들어, LINQ to Entity Framework 쿼리 공급자는 식 트리를 데이터베이스로 직접 변환하여 SQL로 변환합니다.

모든 조각을 모으면 LINQ의 진정한 힘을 알 수 있습니다.

  1. 람다 식을 사용하여 쿼리를 작성하십시오 : products.Where(x => x.Cost > 5)
  2. 컴파일러는 "매개 변수의 Cost 속성이 5보다 큰지 확인"지침에 따라 해당 식을 표현식 트리로 변환합니다.
  3. 쿼리 공급자가 식 트리를 구문 분석하고 유효한 SQL 쿼리를 만듭니다. SELECT * FROM products WHERE Cost > 5
  4. ORM은 모든 결과를 POCO에 투영하고 객체 목록을 얻습니다.

노트

  • 표현식 트리는 변경 불가능합니다. 표현식 트리를 변경하려면 새로운 표현식 트리를 만들고 기존 표현식 트리를 새로운 표현식 트리에 복사 한 다음 ( ExpressionVisitor 사용할 수 있도록 ExpressionVisitor 사용할 수 있음) 원하는 변경을하십시오.

표현식 트리 관련 예