[C#] Func <T> 대신 Expression <Func <T >>를 사용하는 이유는 무엇입니까?

나는 람다와 이해 FuncAction대의원. 그러나 표정은 저를 ump니다.

어떤 상황 Expression<Func<T>>에서 평범하지 않은 오래된 것을 사용 Func<T>하시겠습니까?



답변

람다 식을 식 트리로 취급하고 실행하지 말고 내부를 살펴보십시오. 예를 들어 LINQ to SQL은 표현식을 가져 와서 동등한 SQL 문으로 변환하여 람다를 실행하지 않고 서버에 제출합니다.

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

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 –SQL에가 필요한 이유가 이해가 명확하게 Expression하고,이 Func적합하지 않습니다. FuncSQL / MongoDb / 기타 쿼리로 변환하는 방법의 핵심을 이해하기 위해 자체적으로 들어가는 방법을 가지고 있지 않습니다. 더하기 또는 곱하기 또는 빼기를 수행하는지 확인할 수 없습니다. 당신이 할 수있는 것은 그것을 실행하는 것입니다. Expression반면, 델리게이트 내부를보고 원하는 모든 것을 볼 수 있습니다. 이렇게하면 대리자를 SQL 쿼리와 같이 원하는대로 변환 할 수 있습니다. Func내 DbContext가 람다 식의 내용에 대해 눈이 멀었 기 때문에 작동하지 않았습니다. 이 때문에 람다 식을 SQL로 바꿀 수 없었습니다. 그러나 다음으로 최선을 다했으며 내 테이블의 각 행을 통해 조건을 반복했습니다.

편집 : John Peter의 요청에 따라 마지막 문장에 대해 설명합니다.

IQueryable은 IEnumerable을 확장하므로 IEnumerable의 메서드는 Where()을 허용하는 오버로드를 얻습니다 Expression. 에 전달하면 Expression결과로 IQueryable을 유지하지만을 전달 Func하면 기본 IEnumerable로 돌아가서 IEnumerable을 얻게됩니다. 다시 말해, 주목할 필요없이 데이터 세트를 쿼리 할 대상이 아닌 반복 할 목록으로 전환했습니다. 서명을 실제로 볼 때까지 차이를 느끼기가 어렵습니다.


답변

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

Entity Framework를 통한 Expression vs Func
LINQ와의 사랑 에 대한 자세한 내용 -7 부 : 표현식 및 기능 (마지막 섹션)


답변

나는 사이의 차이점에 대한 몇 가지 메모를 추가하고 싶습니다 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 the Patterns for Reusable .NET Libraries ) 에서 더 철학적으로 설명 할 수 있습니다 .

리코 마리아니

이미지가 아닌 버전으로 편집 :

대부분의 경우 필요한 모든 코드를 실행하는 것이라면 Func 또는 Action 을 원할 것입니다. 코드를 실행하기 전에 코드를 분석, 직렬화 또는 최적화 해야하는 경우 Expression 이 필요 합니다. Expression 은 코드를 생각 하기위한 것이고 Func / Action 은 코드 를 실행하기위한 것입니다.


답변

LINQ는 정식 예제 (예 : 데이터베이스와 대화)이지만 실제로는 실제로 수행하는 것이 아니라 수행 할 작업을 표현 하는 데 더 관심이있는 경우 언제든지 가능 합니다. 예를 들어, 나는 코드 생성 등을 피하기 위해 protobuf-net 의 RPC 스택 에서이 접근법을 사용 하므로 다음과 같이 메소드를 호출하십시오.

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

이것은 해결하기 위해 식 트리 해체 SomeMethod(각 인수의 값), 수행 RPC 호출 모든 업데이트 ref/의 out인수를 원격 호출의 결과를 반환합니다. 이것은 표현식 트리를 통해서만 가능합니다. 나는 이것을 더 많이 여기에서 다룬다 .

또 다른 예는 일반 연산자 코드 에서와 같이 람다로 컴파일하기 위해 식 트리를 수동으로 작성하는 경우 입니다.


답변

함수를 코드가 아닌 데이터로 취급하려는 경우 표현식을 사용합니다. 코드를 데이터로 조작하려는 경우이 작업을 수행 할 수 있습니다. 대부분의 경우 표현식이 필요하지 않으면 표현식을 사용할 필요가 없습니다.