developer tip

Expression을 사용하는 이유

copycodes 2020. 9. 28. 09:19
반응형

Expression을 사용하는 이유> Func보다는?


나는 람다와 이해 FuncAction대의원. 그러나 표현은 나를 괴롭힌다. 어떤 상황 Expression<Func<T>>에서 평범한 오래된 것보다는를 사용 Func<T>하시겠습니까?


람다 식을 식 트리로 취급하고 실행하는 대신 내부를 살펴보고 싶을 때. 예를 들어 LINQ to SQL은 식을 가져 와서 동등한 SQL 문으로 변환하고이를 서버에 제출합니다 (람다를 실행하는 대신).

개념적으로, Expression<Func<T>>이다 완전히 다른 에서 Func<T>. Func<T>나타내고 delegate거의 방법에 대한 포인터이며, 이는 Expression<Func<T>>나타낸다 트리 데이터 구조를 람다 표현. 이 트리 구조 는 실제 작업을 수행하는 대신 람다 표현식이 수행하는 작업을 설명합니다. 기본적으로 표현식, 변수, 메서드 호출 등의 구성에 대한 데이터를 보유합니다 (예를 들어이 람다는 상수 + 일부 매개 변수와 같은 정보를 보유합니다). 이 설명을 사용하여 실제 메서드 (사용 Expression.Compile) 로 변환 하거나 LINQ to SQL 예제와 같은 다른 작업을 수행 할 수 있습니다. 람다를 익명 메서드 및 표현식 트리로 취급하는 행위는 순전히 컴파일 시간 문제입니다.

Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }

아무것도 얻지 못하고 10을 반환하는 IL 메서드로 효과적으로 컴파일됩니다.

Expression<Func<int>> myExpression = () => 10;

매개 변수가없고 값 10을 리턴하는 표현식을 설명하는 데이터 구조로 변환됩니다.

식 대 기능 큰 이미지

둘 다 컴파일 타임에 동일하게 보이지만 컴파일러가 생성하는 것은 완전히 다릅니다 .


나는 그것이 얼마나 간단한 지 깨달을 때 까지이 대답이 내 머리 위로 보였기 때문에 멍청한 대답을 추가하고 있습니다. 때때로 그것은 당신이 '그 주위를 머리를 감쌀'수 없게 만드는 것이 복잡하다는 당신의 기대입니다.

LINQ-to-SQL을 일반적으로 사용하려는 정말 성가신 '버그'에 들어갈 때까지 차이점을 이해할 필요가 없었습니다.

public IEnumerable<T> Get(Func<T, bool> conditionLambda){
  using(var db = new DbContext()){
    return db.Set<T>.Where(conditionLambda);
  }
}

이것은 큰 데이터 세트에서 OutofMemoryExceptions를 받기 ​​시작할 때까지 훌륭하게 작동했습니다. 람다 내부에 중단 점을 설정하면 람다 조건과 일치하는 항목을 찾기 위해 테이블의 각 행을 하나씩 반복하고 있음을 깨달았습니다. LINQ-to-SQL을 수행하는 대신 데이터 테이블을 거대한 IEnumerable로 취급하는 이유는 무엇입니까? LINQ-to-MongoDb 대응 물에서도 똑같은 일을하고있었습니다.

수정 프로그램을 설정하는 간단했다 Func<T, bool>으로 Expression<Func<T, bool>>, 그래서 그것이 필요한 이유 검색 좀 Expression대신을 Func여기서 종료.

식은 단순히 대리자를 자신에 대한 데이터로 변환합니다. 따라서 a => a + 1"왼쪽에는 int a. 오른쪽에는 1을 더합니다."와 같이됩니다. 그게 다야. 이제 집에 갈 수 있습니다. 분명히 그것보다 더 구조화되어 있지만, 본질적으로 모든 표현 트리는 머리를 감쌀 필요가 없습니다.

이를 이해하면 LINQ-to-SQL에 Expression, a Func필요한 이유가 명확 해집니다 . FuncSQL / MongoDb / 기타 쿼리로 변환하는 방법에 대한 핵심을 확인하기위한 방법을 제공하지 않습니다. 덧셈인지 곱셈인지 뺄셈인지 알 수 없습니다. 당신이 할 수있는 것은 그것을 실행하는 것입니다. Expression반면, 델리게이트 내부를보고 원하는 모든 것을 볼 수 있습니다. 이를 통해 대리자를 SQL 쿼리와 같이 원하는대로 변환 할 수 있습니다. Func내 DbContext가 람다 식의 내용을 알지 못하기 때문에 작동하지 않았습니다. 이 때문에 람다 식을 SQL로 변환 할 수 없습니다. 그러나 그것은 차선책을 수행하고 내 테이블의 각 행을 통해 해당 조건을 반복했습니다.

편집 : John Peter의 요청에 따라 내 마지막 문장에 대한 설명 :

IQueryable은 IEnumerable을 확장하므로 IEnumerable의 메서드 Where()Expression. 를 전달하면 Expression결과적으로 IQueryable이 유지되지만를 전달 Func하면 기본 IEnumerable로 되돌아 가고 결과적으로 IEnumerable을 얻게됩니다. 다시 말해, 데이터 세트를 쿼리 할 항목이 아닌 반복 할 목록으로 전환 한 것입니다. 서명을 실제로 살펴보기 전까지는 차이점을 알아 차리기가 어렵습니다.


Expression 대 ​​Func를 선택할 때 매우 중요한 고려 사항은 LINQ to Entities와 같은 IQueryable 공급자가 Expression에서 전달하는 내용을 '다이제스트'할 수 있지만 Func에서 전달하는 내용은 무시한다는 것입니다. 주제에 대한 두 개의 블로그 게시물이 있습니다.

Entity Framework를 사용한 Expression vs Func에 대한 추가 정보LINQ와 사랑에 빠지기-Part 7 : Expressions and Funcs (마지막 섹션)


나는 사이의 차이점에 대한 몇 가지 메모를 추가하고 싶습니다 Func<T>Expression<Func<T>>:

  • Func<T> 일반적인 구식 MulticastDelegate입니다.
  • Expression<Func<T>> 람다 표현을 표현 트리 형태로 표현한 것입니다.
  • 식 트리는 람다 식 구문 또는 API 구문을 통해 구성 할 수 있습니다.
  • 식 트리를 대리자로 컴파일 할 수 있습니다 Func<T>.
  • 역변환은 이론적으로 가능하지만 일종의 디 컴파일이며, 간단한 프로세스가 아니기 때문에 내장 된 기능이 없습니다.
  • 표현식 트리는 다음을 통해 관찰 / 번역 / 수정할 수 있습니다 ExpressionVisitor.
  • IEnumerable의 확장 메서드는 다음과 함께 작동합니다 Func<T>.
  • IQueryable의 확장 메서드는 Expression<Func<T>>.


LINQ : Func <T> vs. Expression <Func <T >> 코드 샘플과 함께 세부 사항을 설명하는 기사가 있습니다 .

도움이 되길 바랍니다.


Krzysztof Cwalina의 저서 ( Framework Design Guidelines : Conventions, Idioms and Patterns for Reusable .NET Libraries ) 에서 이에 대한 더 철학적 인 설명이 있습니다 .

리코 마리아니

이미지가 아닌 버전 편집 :

Most times you're going to want Func or Action if all that needs to happen is to run some code. You need Expression when the code needs to be analyzed, serialized, or optimized before it is run. Expression is for thinking about code, Func/Action is for running it.


LINQ is the canonical example (for example, talking to a database), but in truth, any time you care more about expressing what to do, rather than actually doing it. For example, I use this approach in the RPC stack of protobuf-net (to avoid code-generation etc) - so you call a method with:

string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));

This deconstructs the expression tree to resolve SomeMethod (and the value of each argument), performs the RPC call, updates any ref/out args, and returns the result from the remote call. This is only possible via the expression tree. I cover this more here.

Another example is when you are building the expression trees manually for the purpose of compiling to a lambda, as done by the generic operators code.


You would use an expression when you want to treat your function as data and not as code. You can do this if you want to manipulate the code (as data). Most of the time if you don't see a need for expressions then you probably don't need to use one.


The primary reason is when you don't want to run the code directly, but rather, want to inspect it. This can be for any number of reasons:

  • Mapping the code to a different environment (ie. C# code to SQL in Entity Framework)
  • Replacing parts of the code in runtime (dynamic programming or even plain DRY techniques)
  • Code validation (very useful when emulating scripting or when doing analysis)
  • Serialization - expressions can be serialized rather easily and safely, delegates can't
  • Strongly-typed safety on things that aren't inherently strongly-typed, and exploiting compiler checks even though you're doing dynamic calls in runtime (ASP.NET MVC 5 with Razor is a nice example)

아직 성능에 대한 답변이 없습니다. Func<>s를 Where()또는 전달하는 Count()것은 좋지 않습니다. 정말 나쁘다. a를 사용하면 대신 LINQ 항목을 Func<>호출하므로 전체 테이블이 가져온 다음 필터링됩니다. 특히 다른 서버에있는 데이터베이스를 쿼리하는 경우 훨씬 빠릅니다.IEnumerableIQueryableExpression<Func<>>

참고 URL : https://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct

반응형