[c#] .NET Framework에서 람다와 대리자의 차이점은 무엇입니까?

나는이 질문을 많이 받고 차이점을 가장 잘 설명하는 방법에 대한 의견을 구할 것이라고 생각했습니다.



답변

그들은 실제로 두 가지 매우 다른 것입니다. “대리자”는 실제로 메서드 또는 람다에 대한 참조를 보유하는 변수의 이름이고 람다는 영구 이름이없는 메서드입니다.

Lambda는 몇 가지 미묘한 차이점을 제외하고는 다른 방법과 매우 유사합니다.

  1. 일반 메서드는 “문”으로 정의되고 영구 이름에 연결되는 반면 람다는 “표현식” 에서 즉시”정의되며 영구 이름이 없습니다.
  2. 일부 람다는 .NET 식 트리와 함께 사용할 수 있지만 메서드는 사용할 수 없습니다.

대리자는 다음과 같이 정의됩니다.

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

BinaryIntOp 유형의 변수는 서명이 동일한 한 메서드 또는 labmda를 할당 할 수 있습니다. 두 개의 Int32 인수와 하나의 Int32 반환.

람다는 다음과 같이 정의 될 수 있습니다.

BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;

주목해야 할 또 다른 점은 일반 Func 및 Action 유형이 종종 “람다 유형”으로 간주되지만 다른 대리자와 동일하다는 것입니다. 이것에 대한 좋은 점은 기본적으로 필요한 모든 유형의 델리게이트에 대한 이름을 정의한다는 것입니다 (최대 4 개의 매개 변수, 물론 더 많은 매개 변수를 추가 할 수 있음). 따라서 다양한 대리자 유형을 사용하지만 한 번만 사용하는 경우 Func 및 Action을 사용하여 대리자 선언으로 코드가 복잡 해지는 것을 방지 할 수 있습니다.

다음은 Func와 Action이 “람다만을위한 것이 아님”에 대한 설명입니다.

Int32 DiffOfSquares(Int32 x, Int32 y)
{
  return x*x - y*y;
}

Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;

알아 두어야 할 또 다른 유용한 점은 서명은 같지만 이름이 다른 대리자 형식 (메서드 자체가 아님)은 서로 암시 적으로 캐스팅되지 않는다는 것입니다. 여기에는 Func 및 Action 대리자가 포함됩니다. 그러나 서명이 동일하면 둘 사이에 명시 적으로 캐스팅 할 수 있습니다.

더 나아 가기 …. C #에서 함수는 람다와 대리자를 사용하여 유연합니다. 그러나 C #에는 “일급 함수”가 없습니다. 델리게이트 변수에 할당 된 함수의 이름을 사용하여 본질적으로 해당 함수를 나타내는 객체를 만들 수 있습니다. 그러나 이것은 실제로 컴파일러 트릭입니다. 함수 이름 뒤에 점을 작성하여 문을 시작하면 (즉, 함수 자체에서 멤버 액세스를 시도) 참조 할 멤버가 없음을 알 수 있습니다. Object의 것도 아닙니다. 이것은 프로그래머가 모든 함수에서 호출 될 수있는 확장 메서드를 추가하는 것과 같은 유용한 (물론 잠재적으로 위험한) 일을하는 것을 방지합니다. 당신이 할 수있는 최선의 방법은 Delegate 클래스 자체를 확장하는 것입니다. 이것은 확실히 유용하지만 그다지 많지는 않습니다.

업데이트 : 익명 대리자와 메서드 및 람다의 차이점을 보여주는 Karg의 답변 도 참조하십시오 .

업데이트 2 : James Hart 는 매우 기술적이지만 중요하지만 람다와 델리게이트는 .NET 엔터티가 아니라 (즉, CLR에는 델리게이트 또는 람다의 개념이 없음) 오히려 프레임 워크 및 언어 구성이라는 점에 유의합니다.


답변

질문은 약간 모호하며, 이는 귀하가받는 답변의 광범위한 불일치를 설명합니다.

실제로 .NET 프레임 워크에서 람다와 대리자의 차이점이 무엇인지 물었습니다. 여러 가지 중 하나 일 수 있습니다. 당신이 묻고 있습니까?

  • C # (또는 VB.NET) 언어에서 람다 식과 익명 대리자의 차이점은 무엇입니까?

  • .NET 3.5에서 System.Linq.Expressions.LambdaExpression 개체와 System.Delegate 개체의 차이점은 무엇입니까?

  • 아니면 그 극단 사이 또는 그 주변 어딘가에 있습니까?

어떤 사람들은 ‘C # Lambda 표현식과 .NET System.Delegate의 차이점은 무엇입니까?’라는 질문에 대한 답을 제공하려고하는 것 같습니다.

.NET 프레임 워크 자체는 익명 대리자, 람다 식 또는 클로저의 개념을 이해하지 못합니다. 이러한 개념은 모두 언어 사양에 정의 된 것입니다. C # 컴파일러가 익명 ​​메서드의 정의를 멤버 변수를 사용하여 생성 된 클래스의 메서드로 변환하여 클로저 상태를 유지하는 방법에 대해 생각해보십시오. .NET에서는 대리자에 대해 익명이 없습니다. 작성하는 C # 프로그래머에게는 익명입니다. 대리자 형식에 할당 된 람다 식의 경우에도 마찬가지입니다.

무엇 .NET 않는이 방법 서명을 설명하는 유형 중 하나에 대해 호출 할 수있는 특정 유형의 특정 방법에 특정 개체 또는 언 바운드 통화에 대한 구체적인 방법에 대한 호출을 바인딩 표현한다 인스턴스있는 -을 이해하는 것은 대표의 생각이다 해당 유형의 모든 객체, 여기서 상기 방법은 상기 서명을 준수합니다. 이러한 유형은 모두 System.Delegate에서 상속됩니다.

.NET 3.5는 또한 코드 식을 설명하기위한 클래스를 포함하는 System.Linq.Expressions 네임 스페이스를 도입했습니다. 따라서 특정 형식 또는 개체에 대한 메서드에 대한 바인딩되거나 바인딩되지 않은 호출을 나타낼 수도 있습니다. 그런 다음 LambdaExpression 인스턴스를 실제 대리자로 컴파일 할 수 있습니다 (식 구조를 기반으로하는 동적 메서드가 코딩되고 이에 대한 대리자 포인터가 반환 됨).

C #에서는 해당 유형의 변수에 람다 식을 할당하여 System.Expressions.Expression 유형의 인스턴스를 생성 할 수 있습니다. 그러면 런타임에 식을 생성하는 데 적합한 코드가 생성됩니다.

물론, 당신이 경우 차이가 람다 표현식과 C #의 익명 메소드 사이에 무엇을 물어 ‘, 결국, 모든이 거의 irelevant이며,이 경우 주 차이는 익명의 대의원을 향해 몸을 숙 당신이 돈 때하는 간결하다 매개 변수를 신경 쓰지 말고 값을 반환 할 계획을 세우지 말고 유형 추론 매개 변수와 반환 유형을 원할 때 람다를 사용하세요.

그리고 람다 식은 식 생성을 지원합니다.


답변

한 가지 차이점은 익명 대리자는 매개 변수를 생략 할 수 있지만 람다는 정확한 서명과 일치해야한다는 것입니다. 주어진:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

다음 네 가지 방법으로 호출 할 수 있습니다 (두 번째 줄에는 매개 변수가없는 익명 대리자가 있음).

Test(delegate(int i) { return String.Empty; });
Test(delegate { return String.Empty; });
Test(i => String.Empty);
Test(D);

private string D(int i)
{
    return String.Empty;
}

매개 변수가없는 람다 식이나 매개 변수가없는 메서드는 전달할 수 없습니다. 다음은 허용되지 않습니다.

Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature

private string D2()
{
    return String.Empty;
}


답변

대리자는 함수 포인터 / 메소드 포인터 / 콜백 (선택)과 동일하며 람다는 매우 단순화 된 익명 함수입니다. 적어도 나는 사람들에게 그렇게 말합니다.


답변

나는 이것에 대해 많은 경험이 없지만 델리게이트는 모든 함수를 감싸는 래퍼이고 람다 식 자체는 익명 함수라는 것입니다.


답변

대리자는 항상 기본적으로 함수 포인터입니다. 람다는 대리자로 바뀔 수 있지만 LINQ 식 트리로도 바뀔 수도 있습니다. 예를 들어

Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;

첫 번째 줄은 대리자를 생성하고 두 번째 줄은 식 트리를 생성합니다.


답변

람다는 단순히 델리게이트의 구문 설탕입니다. 컴파일러는 람다를 대리자로 변환합니다.

이것들은 똑같습니다.

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};