나는 람다와 이해 Func
및 Action
대의원. 그러나 표정은 저를 ump니다.
어떤 상황 Expression<Func<T>>
에서 평범하지 않은 오래된 것을 사용 Func<T>
하시겠습니까?
나는 람다와 이해 Func
및 Action
대의원. 그러나 표정은 저를 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을 리턴하는 표현식을 설명하는 데이터 구조로 변환됩니다.
컴파일 타임에서 둘 다 동일하게 보이지만 컴파일러가 생성하는 것은 완전히 다릅니다 .
Expression
에 특정 대리인에 대한 메타 정보 가 포함되어 있습니다.
Expression<Func<...>>
그냥 사용하는 대신 대리인이 존재하지 않습니다 Func<...>
.
(isAnExample) => { if(isAnExample) ok(); else expandAnswer(); }
와 같은 표현식은 ExpressionTree이며, 분기는 If 문에 대해 작성됩니다.
이 답변이 얼마나 간단한 지 깨달을 때까지 답이 내 머리 위로 보였으므로 답변에 대한 답변을 추가하고 있습니다. 때로는 머리를 감쌀 수 없게 만드는 것이 복잡하다는 기대가 있습니다.
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
적합하지 않습니다. Func
SQL / 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>>
는 표현 트리 형태의 람다 표현의 표현이고;Func<T>
.ExpressionVisitor
;Func<T>
.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 은 코드 를 실행하기위한 것입니다.
database.data.Where(i => i.Id > 0)
로 실행 해야 합니다 SELECT FROM [data] WHERE [id] > 0
. 당신은 단지 Func을 전달 경우에, 당신은 당신의 드라이버에 눈가리개를 넣었습니다 그것이 할 수있는 모든이다 SELECT *
ID> 0이 포장과 함께 모든에서 각각의 필터를 통해 반복,을 한 후 메모리에 데이터를 모두 장전 한 번 Func
에 Expression
힘을 실어 드라이버를 분석하여 Func
Sql / MongoDb / other 쿼리로 변환합니다.
Expression
하려고하지만 휴가 중일 때는 Func/Action
;)
LINQ는 정식 예제 (예 : 데이터베이스와 대화)이지만 실제로는 실제로 수행하는 것이 아니라 수행 할 작업을 표현 하는 데 더 관심이있는 경우 언제든지 가능 합니다. 예를 들어, 나는 코드 생성 등을 피하기 위해 protobuf-net 의 RPC 스택 에서이 접근법을 사용 하므로 다음과 같이 메소드를 호출하십시오.
string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
이것은 해결하기 위해 식 트리 해체 SomeMethod
(각 인수의 값), 수행 RPC 호출 모든 업데이트 ref
/의 out
인수를 원격 호출의 결과를 반환합니다. 이것은 표현식 트리를 통해서만 가능합니다. 나는 이것을 더 많이 여기에서 다룬다 .
또 다른 예는 일반 연산자 코드 에서와 같이 람다로 컴파일하기 위해 식 트리를 수동으로 작성하는 경우 입니다.
주된 이유는 코드를 직접 실행하지 않고 검사하려는 경우입니다. 여러 가지 이유가있을 수 있습니다.
Expression
식은 임의의 대리자 / 메서드 참조의 호출을 포함 할 수 있으므로 대리자처럼 직렬화하는 것이 불가능할 수 있습니다. "쉬운"은 물론 상대적입니다.
성능에 대한 답변이 아직 없습니다. 전달 Func<>
에들 Where()
또는 것은 Count()
좋지 않습니다. 진짜 나빠 당신이 사용하는 경우 Func<>
다음이 호출 IEnumerable
대신 LINQ 물건 IQueryable
전체 테이블에서 뽑아 얻을 것을 의미 한 후 여과한다. Expression<Func<>>
특히 다른 서버에있는 데이터베이스를 쿼리하는 경우 훨씬 빠릅니다.