문제

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

도움이 되었습니까?

해결책

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

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

  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 클래스 자체를 확장하는 것입니다. 이 역시 유용하지만 그다지 많지는 않습니다.

업데이트:또한 참조하십시오 카르그의 대답 익명 대리인과 대리인 간의 차이점을 보여줍니다.메소드와 람다.

업데이트 2: 제임스 하트 기술적으로는 매우 중요하지만 람다와 대리자가 .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#에서 람다 식과 익명 메서드의 차이점이 무엇인지 묻는다면 결국 이 모든 것은 거의 관련이 없으며 이 경우 주요 차이점은 간결성입니다. 유형 유추 매개 변수 및 반환 유형을 원할 때 값 반환을 계획하지 않고 람다를 사용합니다.

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

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

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";};

대리자는 함수 서명입니다.같은 것

delegate string MyDelegate(int param1);

대리자는 본문을 구현하지 않습니다.

람다는 대리자의 서명과 일치하는 함수 호출입니다.위 대리자의 경우 다음 중 하나를 사용할 수 있습니다.

(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";

그만큼 Delegate 하지만 유형의 이름이 잘못되었습니다.유형의 객체 생성 Delegate 실제로 람다, 정적 메서드, 클래스 메서드 등 함수를 보유할 수 있는 변수를 생성합니다.

대리자는 특정 매개변수 목록과 반환 유형이 있는 메서드에 대한 참조입니다.객체를 포함할 수도 있고 포함하지 않을 수도 있습니다.

람다 표현식은 익명 함수의 한 형태입니다.

질문은 "람다와 람다의 차이점은 무엇입니까?"라는 것이 분명합니다. 익명의 대리자?" 여기의 모든 답변 중에서 단 한 사람만이 정답을 맞췄습니다. 가장 큰 차이점은 람다를 사용하여 대리자뿐만 아니라 식 트리도 만들 수 있다는 것입니다.

MSDN에서 자세한 내용을 읽을 수 있습니다. http://msdn.microsoft.com/en-us/library/bb397687.aspx

대리자는 실제로 기능에 대한 구조적 유형 지정일 뿐입니다.명목 유형 지정 및 인터페이스나 추상 클래스를 구현하는 익명 클래스 구현을 사용하여 동일한 작업을 수행할 수 있지만, 함수가 하나만 필요한 경우 결국 많은 코드가 필요하게 됩니다.

람다(Lambda)는 1930년대 알론조 교회(Alonzo Church)의 람다 계산(lambda calculus) 아이디어에서 유래되었습니다.함수를 생성하는 익명의 방법입니다.함수를 구성하는 데 특히 유용합니다.

따라서 어떤 사람들은 람다가 대리자를 위한 구문적 설탕이라고 말할 수도 있지만 저는 대리자가 C#에서 사람들을 람다로 쉽게 전환할 수 있는 다리라고 말하고 싶습니다.

대리자는 함수 포인터의 대기열이며 대리자를 호출하면 여러 메서드를 호출할 수 있습니다.람다는 본질적으로 사용되는 컨텍스트에 따라 컴파일러에서 다르게 해석될 수 있는 익명 메서드 선언입니다.

대리자로 캐스팅하여 람다 식을 메서드로 가리키는 대리자를 얻을 수 있습니다. 또는 특정 대리자 형식을 기대하는 메서드에 매개 변수로 전달하는 경우 컴파일러가 이를 캐스팅합니다.LINQ 문 내에서 이를 사용하면 람다는 컴파일러에 의해 단순히 대리자가 아닌 식 트리로 변환됩니다.

실제로 차이점은 람다가 다른 식 내부에 메서드를 정의하는 간결한 방법인 반면 대리자는 실제 개체 유형이라는 것입니다.

람다는 단순화된 버전의 대리자입니다.그들은 다음과 같은 속성 중 일부를 가지고 있습니다. 폐쇄 익명 대리인과 같지만 암시적 입력을 사용할 수도 있습니다.람다는 다음과 같습니다.

something.Sort((x, y) => return x.CompareTo(y));

대리자로 할 수 있는 것보다 훨씬 더 간결합니다.

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)
{
    one.CompareTo(two)
}

여기 제가 블로그에 잠시 올렸던 예가 있습니다.작업자 스레드에서 레이블을 업데이트하고 싶다고 가정해 보겠습니다.대리자, 익명 대리자 및 2가지 유형의 람다를 사용하여 해당 레이블을 1에서 50으로 업데이트하는 방법에 대한 4가지 예가 있습니다.

 private void button2_Click(object sender, EventArgs e) 
     { 
         BackgroundWorker worker = new BackgroundWorker(); 
         worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
         worker.RunWorkerAsync(); 
     } 

     private delegate void UpdateProgDelegate(int count); 
     private void UpdateText(int count) 
     { 
         if (this.lblTest.InvokeRequired) 
         { 
             UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText); 
             this.Invoke(updateCallBack, new object[] { count }); 
         } 
         else 
         { 
             lblTest.Text = count.ToString(); 
         } 
     } 

     void worker_DoWork(object sender, DoWorkEventArgs e) 
     {   
         /* Old Skool delegate usage.  See above for delegate and method definitions */ 
         for (int i = 0; i < 50; i++) 
         { 
             UpdateText(i); 
             Thread.Sleep(50); 
         } 

         // Anonymous Method 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((MethodInvoker)(delegate() 
             { 
                 lblTest.Text = i.ToString(); 
             })); 
             Thread.Sleep(50); 
         } 

         /* Lambda using the new Func delegate. This lets us take in an int and 
          * return a string.  The last parameter is the return type. so 
          * So Func<int, string, double> would take in an int and a string 
          * and return a double.  count is our int parameter.*/ 
         Func<int, string> UpdateProgress = (count) => lblTest.Text = count.ToString(); 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke(UpdateProgress, i); 
             Thread.Sleep(50); 
         } 

         /* Finally we have a totally inline Lambda using the Action delegate 
          * Action is more or less the same as Func but it returns void. We could 
          * use it with parameters if we wanted to like this: 
          * Action<string> UpdateProgress = (count) => lblT…*/ 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((Action)(() => lblTest.Text = i.ToString())); 
             Thread.Sleep(50); 
         } 
     }

귀하의 질문은 .NET이 아닌 C#에 관한 것이라고 가정합니다. 귀하의 질문이 모호하기 때문에 .NET은 단독으로 작동하지 않습니다. 즉, C# 없이는 대리자와 람다 식을 이해할 수 없습니다.

ㅏ (정상, 이른바 반대 일반적인 대표자, CF 나중에) 위임은 일종의 C++로 간주되어야 합니다. typedef 예를 들어 C++의 함수 포인터 유형:

R (*thefunctionpointer) ( T ) ;

typedef는 유형입니다 thefunctionpointer 이는 유형의 객체를 취하는 함수에 대한 포인터 유형입니다. T 유형의 객체를 반환합니다. R.다음과 같이 사용합니다.

thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T

어디 thefunction 다음을 수행하는 함수가 될 것입니다. T 그리고 반환 R.

C #에서는 갈 것입니다

delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed

그리고 당신은 그것을 다음과 같이 사용할 것입니다 :

thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T

어디 thefunction 다음을 수행하는 함수가 될 것입니다. T 그리고 반환 R.이는 소위 일반 대의원이라고 불리는 대의원을 위한 것입니다.

이제 C#에는 일반 대리자도 있습니다. 즉. 말하자면 "템플릿화"되어 C++ 표현식을 사용하는 것입니다.그것들은 다음과 같이 정의됩니다:

public delegate TResult Func<in T, out TResult>(T arg);

그리고 다음과 같이 사용할 수 있습니다.

Func<double, double> thefunctor = thefunction2; // call it a functor because it is
                                                // really as a functor that you should
                                                // "see" it
double y = thefunctor(2.0);

어디 thefunction2 인수로 받아 다음을 반환하는 함수입니다. double.

이제 대신에 그것을 상상해보십시오. thefunction2 나는 지금은 어디에도 정의되어 있지 않고 나중에는 절대 사용하지 않을 "함수"를 사용하고 싶습니다.그런 다음 C#을 사용하면 표현 이 기능의.표현이란 "수학적"(또는 프로그램에 충실하려면 기능적) 표현을 의미합니다. 예를 들면 다음과 같습니다.에 double x 그럴게요 연관짓다 그만큼 double x*x.수학에서는 다음을 사용하여 이것을 씁니다. "\mapsto" 라텍스 기호.C#에서는 함수형 표기법을 차용했습니다. =>.예를 들어 :

Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
                                                           // mandatory

(double x) => x * x 이다 표현.이는 유형이 아니지만 대리자(일반 여부)는 유형입니다.

도덕성?마지막으로 델리게이트(resp.일반 대리자), 함수 포인터 유형이 아닌 경우(resp.래핑+스마트+일반 함수 포인터 유형), 응?다른 것 !보다 이것 그리고 저것.

여기에 몇 가지 기본 사항이 있습니다."대리인"은 실제로 메서드나 람다에 대한 참조를 보유하는 변수의 이름입니다.

이것은 익명의 방법입니다 -

(string testString) => { Console.WriteLine(testString); };

무명 메서드에는 이름이 없으므로 이러한 메서드나 식을 모두 할당할 수 있는 대리자가 필요합니다.예를 들어.

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

람다 표현식과 동일합니다.일반적으로 이를 사용하려면 대리인이 필요합니다.

s => s.Age > someValue && s.Age < someValue    // will return true/false

이 표현식을 사용하려면 func 대리자를 사용할 수 있습니다.

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);

음, 정말 지나치게 단순화된 버전은 람다가 익명 함수의 약어일 뿐이라는 것입니다.위임자는 익명 기능보다 더 많은 일을 할 수 있습니다:이벤트, 비동기 호출, 다중 메서드 체인 등이 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top