다음을 통해 공유


대리자 및 람다 식

대리자는 특정 매개 변수 목록 및 반환 형식이 있는 메서드에 대한 참조를 나타내는 형식을 정의합니다. 매개 변수 목록과 반환 형식이 일치하는 메서드(정적 또는 인스턴스)를 해당 형식의 변수에 할당한 다음 직접 호출하거나(적절한 인수를 사용하여) 인수 자체로 다른 메서드에 전달한 다음 호출할 수 있습니다. 다음 예제에서는 대리자 사용을 보여 줍니다.

using System;
using System.Linq;

public class Program
{
    public delegate string Reverse(string s);

    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Reverse rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}
  • 줄은 public delegate string Reverse(string s); 문자열 매개 변수를 사용한 다음 문자열 매개 변수를 반환하는 메서드의 대리자 형식을 만듭니다.
  • static string ReverseString(string s) 정의된 대리자 형식과 정확히 동일한 매개 변수 목록 및 반환 형식을 포함하는 메서드는 대리자를 구현합니다.
  • 줄은 Reverse rev = ReverseString; 해당 대리자 형식의 변수에 메서드를 할당할 수 있음을 보여줍니다.
  • 이 줄은 Console.WriteLine(rev("a string")); 대리자 형식의 변수를 사용하여 대리자를 호출하는 방법을 보여 줍니다.

개발 프로세스를 간소화하기 위해 .NET에는 프로그래머가 재사용할 수 있고 새 형식을 만들 필요가 없는 대리자 형식 집합이 포함되어 있습니다. 이러한 형식은 Func<>, Action<>, Predicate<>이며, 새 대리자 형식을 정의하지 않고도 사용할 수 있습니다. 사용하려는 방식과 관련이 있는 세 가지 형식 간에는 몇 가지 차이점이 있습니다.

  • Action<> 는 대리자의 인수를 사용하여 작업을 수행해야 하는 경우에 사용됩니다. 캡슐화하는 메서드는 값을 반환하지 않습니다.
  • Func<> 는 일반적으로 변환이 있을 때 사용됩니다. 즉, 대리자의 인수를 다른 결과로 변환해야 합니다. 프로젝션이 좋은 예입니다. 캡슐화하는 메서드는 지정된 값을 반환합니다.
  • Predicate<> 는 인수가 대리자의 조건을 충족하는지 확인해야 할 때 사용됩니다. Func<T, bool>로도 작성할 수 있으며, 이는 메서드가 불린 값을 반환함을 의미합니다.

이제 위의 예제를 가져와서 사용자 지정 형식 대신 대리자를 Func<> 사용하여 다시 작성할 수 있습니다. 프로그램은 정확히 동일하게 계속 실행됩니다.

using System;
using System.Linq;

public class Program
{
    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Func<string, string> rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}

이 간단한 예제에서는 Main 메서드 외부에서 메서드를 정의하는 것이 약간 불필요한 것처럼 보입니다. .NET Framework 2.0에서는 추가 형식이나 메서드를 지정하지 않고도 "인라인" 대리자를 만들 수 있는 익명 대리자의 개념을 도입했습니다.

다음 예제에서 익명 대리자는 목록을 짝수로 필터링한 다음 콘솔에 출력합니다.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(
          delegate (int no)
          {
              return (no % 2 == 0);
          }
        );

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

보시다시피 대리자의 본문은 다른 대리자처럼 여러 표현의 집합입니다. 그러나 별도의 정의가 아니라 메서드 호출 에서 List<T>.FindAll로 도입했습니다.

그러나 이 방법을 사용하더라도 버릴 수 있는 코드는 여전히 많습니다. 람다 식이 활용되는 곳입니다. 람다 식 또는 간단히 말해서 "람다"는 C# 3.0에서 LINQ(언어 통합 쿼리)의 핵심 구성 요소 중 하나로 도입되었습니다. 대리자를 사용하기 위한 보다 편리한 구문일 뿐입니다. 매개 변수 목록 및 메서드 본문을 선언하지만 대리자에게 할당되지 않는 한 자체의 공식 ID가 없습니다. 대리자와 달리, 이들을 이벤트 등록 시 또는 다양한 LINQ 절 및 메서드에서 직접 할당할 수 있습니다.

람다 식은 대리자를 지정하는 또 다른 방법이기 때문에 위 샘플을 다시 작성하여 익명 대리자 대신 람다 식을 사용할 수 있어야 합니다.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(i => i % 2 == 0);

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

앞의 예제에서 사용된 람다 식은 .입니다 i => i % 2 == 0. 다시 말하지만, 대리자를 사용하기 위한 편리한 구문일 뿐입니다. 내부에서 일어나는 작업은 익명 대리자에서 발생하는 작업과 유사합니다.

다시 말하지만, 람다는 대리자일 뿐입니다. 즉, 다음 코드 조각과 같이 문제 없이 이벤트 처리기로 사용할 수 있습니다.

public MainWindow()
{
    InitializeComponent();

    Loaded += (o, e) =>
    {
        this.Title = "Loaded";
    };
}

+= 이 컨텍스트의 연산자는 이벤트를 구독하는 데 사용됩니다. 자세한 내용은 이벤트구독 및 구독 취소하는 방법을 참조하세요.

추가 읽기 및 리소스