C #에서 대리자를 언제 사용합니까? [닫은]


101

C #에서 델리게이트를 어떻게 사용합니까?


2
.NET 형식 시스템의 대리자 또는 C # 대리자 구문을 의미합니까? "람다 식 구문 대신 대리자 구문을 사용하는 경우"를 의미합니까? 아니면 "클래스 / 인터페이스 / 가상 메서드 / 등 대신 대리자를 사용하는 경우"를 의미합니까?
Niki

답변:


100

이제 C #에 람다 식과 익명 메서드가 있으므로 대리자를 훨씬 더 많이 사용합니다. 논리를 구현하기 위해 항상 별도의 메서드가 있어야하는 C # 1에서는 대리자를 사용하는 것이 종종 의미가 없었습니다. 요즘에는 다음을 위해 대리자를 사용합니다.

  • 이벤트 핸들러 (GUI 등)
  • 시작 스레드
  • 콜백 (예 : 비동기 API)
  • LINQ 및 유사 (List.Find 등)
  • 내부에 특수 논리가 포함 된 "템플릿"코드를 효과적으로 적용하려는 다른 곳 (대리자가 전문화를 제공하는 곳)

푸시 LINQ에서 "푸시"를 언급 할 가치가 있습니까?
Marc Gravell

3
좀 더 혼란스럽게 만들지 않고 간단히 설명 할 수 있을지 모르겠습니다. :) (아마도 이벤트 핸들러, LINQ 및 templaty 비트에 의해 다루어집니다!
Jon Skeet

1
첫 번째 문장은 말이 안됩니다.
senfo

3
당신이 무슨 말을 하려는지는 알고 있지만 다음은 읽기가 더 쉽습니다. "이제 C #에 람다 식과 익명 메서드가 있으므로 대리자를 훨씬 더 많이 사용합니다." 나는 내가 멍청하다고 알고 있지만, 그 문장이 나에게 이해되기 전에 정말 몇 번 읽어야했다.
senfo

4
존경하는 Mr Skeet에게 도전하는 대담한 +1 ;-)
indra

29

대리인은 여러 목적에 매우 유용합니다.

이러한 목적 중 하나는 데이터 시퀀스를 필터링하는 데 사용하는 것입니다. 이 경우 하나의 인수를 받아들이고 대리자 자체의 구현에 따라 true 또는 false를 반환하는 조건 자 대리자를 사용합니다.

여기에 어리석은 예가 있습니다. 이것에서 좀 더 유용한 것을 외삽 할 수 있다고 확신합니다.

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<String> names = new List<String>
        {
            "Nicole Hare",
            "Michael Hare",
            "Joe Hare",
            "Sammy Hare",
            "George Washington",
        };

        // Here I am passing "inMyFamily" to the "Where" extension method
        // on my List<String>.  The C# compiler automatically creates 
        // a delegate instance for me.
        IEnumerable<String> myFamily = names.Where(inMyFamily);

        foreach (String name in myFamily)
            Console.WriteLine(name);
    }

    static Boolean inMyFamily(String name)
    {
        return name.EndsWith("Hare");
    }
}

11
static Boolean inMyFamily(String name)방법 대리자이다. 델리게이트를 매개 변수로 취하는 곳. 델리게이트가되는에 메서드 이름을 전달할 때 델리게이트는 함수 포인터 일뿐 .Where(delegate)입니다. inMyFamily는 부울 유형을 반환하므로 실제로는 술어로 간주됩니다. 술어는 부울을 반환하는 대리자 일뿐입니다.
Landon Poch 2012 년

4
"Predicates는 부울을 반환하는 델리게이트 일뿐입니다." +1
daehaai 2013

@LandonPoch 그 코멘트가 답변에 더 잘 배치되었을 것입니다. 나는 초보자이기 때문에 그것이 어디에 있는지 알 수 없었다. 감사.
Eakan Gopalakrishnan 2014 년

@Eakan, 나는 주요 질문에 실제로 대답하지 않았기 때문에 (언제 대리인을 사용할 것인가) 대신 주석으로 남겨 두었습니다.
Landon Poch 2014 년

14

또 다른 흥미로운 답변을 찾았습니다.

동료가 방금이 질문을했습니다. .NET에서 대리인의 요점은 무엇입니까? 내 대답은 매우 짧았고 그가 온라인에서 찾지 못한 것은 방법의 실행을 지연시키는 것이었다.

출처 : LosTechies

LINQ가하는 것처럼.


+1 .. 그렇게 생각하지 않았습니다. 좋은 점
Luke101

12

대리자를 사용하여 함수 유형 변수 및 매개 변수를 선언 할 수 있습니다.

"자원 차용"패턴을 고려하십시오. 클라이언트 코드가 그 사이에 리소스를 "차용"하도록 허용하면서 리소스 생성 및 정리를 제어하려고합니다.

이것은 대리자 형식을 선언합니다.

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

이 서명과 일치하는 모든 메서드를 사용하여이 형식의 대리자를 인스턴스화 할 수 있습니다. C # 2.0에서는 메서드 이름을 사용하거나 익명 메서드를 사용하여 암시 적으로이 작업을 수행 할 수 있습니다.

이 메소드는 유형을 매개 변수로 사용합니다. 대리자의 호출을 확인합니다.

public class DataProvider
{
    protected string _connectionString;

    public DataProvider( string psConnectionString )
    {
        _connectionString = psConnectionString;
    }

    public void UseReader( string psSELECT, DataReaderUser readerUser )
    {
        using ( SqlConnection connection = new SqlConnection( _connectionString ) )
        try
        {
            SqlCommand command = new SqlCommand( psSELECT, connection );
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();

            while ( reader.Read() )
                readerUser( reader );  // the delegate is invoked
        }
        catch ( System.Exception ex )
        {
            // handle exception
            throw ex;
        }
    }
}

다음과 같이 익명 메서드로 함수를 호출 할 수 있습니다. 익명 메소드는 변수를 사용할 수 있습니다 선언 외부 자체. 이것은 매우 편리합니다 (예제가 약간 인위적이긴하지만).

string sTableName = "test";
string sQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='" + sTableName + "'";

DataProvider.UseReader( sQuery,
    delegate( System.Data.IDataReader reader )
    {
        Console.WriteLine( sTableName + "." + reader[0] );
    } );

11

델리게이트는 종종 하나의 방법으로 인터페이스 대신 사용될 수 있습니다. 일반적인 예로 관찰자 패턴이 있습니다. 다른 언어에서는 어떤 일이 발생했다는 알림을 받으려면 다음과 같이 정의 할 수 있습니다.

class IObserver{ void Notify(...); }

C #에서는 핸들러가 대리자 인 이벤트를 사용하여 더 일반적으로 표현됩니다. 예를 들면 다음과 같습니다.

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

예를 들어 목록에서 항목 집합을 선택하는 경우와 같이 조건자를 함수에 전달해야하는 경우 대리자를 사용하는 또 다른 좋은 장소 :

myList.Where(i => i > 10);

위는 다음과 같이 작성할 수도있는 람다 구문의 예입니다.

myList.Where(delegate(int i){ return i > 10; });

대리자를 사용하는 것이 유용 할 수있는 또 다른 곳은 공장 함수를 등록하는 것입니다. 예를 들면 다음과 같습니다.

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

이게 도움이 되길 바란다!


문법 좋은 예 .. 감사합니다 .. :)
Raghu

10

나는 이것에 대해 정말 늦게 들어 왔지만 오늘 델리게이트의 목적을 파악하는 데 어려움을 겪었고 그들의 목적을 잘 설명하는 동일한 결과를 제공하는 두 개의 간단한 프로그램을 작성했습니다.

NoDelegates.cs

using System;

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Test.checkInt(1);
        Test.checkMax(1);
        Test.checkMin(1);

        Test.checkInt(10);
        Test.checkMax(10);
        Test.checkMin(10);

        Test.checkInt(20);
        Test.checkMax(20);
        Test.checkMin(20);

        Test.checkInt(30);
        Test.checkMax(30);
        Test.checkMin(30);

        Test.checkInt(254);
        Test.checkMax(254);
        Test.checkMin(254);

        Test.checkInt(255);
        Test.checkMax(255);
        Test.checkMin(255);

        Test.checkInt(256);
        Test.checkMax(256);
        Test.checkMin(256);
    }
}

Delegates.cs

using System;

public delegate void Valid(int a);

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Valid v1 = new Valid(Test.checkInt);
        v1 += new Valid(Test.checkMax);
        v1 += new Valid(Test.checkMin);
        v1(1);
        v1(10);
        v1(20);
        v1(30);
        v1(254);
        v1(255);
        v1(256);
    }
}

5

약간 다른 용도는 반사 속도를 높이는 것입니다. 즉, 매번 리플렉션을 사용하는 대신 Delegate.CreateDelegate메서드 (a MethodInfo)에 대한 (형식화 된) 대리자를 만들고 대신 해당 대리자를 호출하는 데 사용할 수 있습니다 . 이것은 다음입니다 많은 검사가 이미 수행 된 것처럼, 빨리 호출 당.

를 사용하면 Expression동일한 작업을 수행하여 즉석에서 코드를 만들 수도 있습니다. 예를 들어 Expression런타임에 선택한 유형에 대한 + 연산자를 나타내는를 쉽게 만들 수 있습니다 (언어가 제공하지 않는 제네릭에 대한 연산자 지원을 제공하기 위해). ; 를 Expression형식화 된 델리게이트로 컴파일 할 수 있습니다 .


5

델리게이트는 이벤트를 사용할 때마다 사용됩니다. 이것이 작동하는 메커니즘입니다.

또한 대리자는 LINQ 쿼리 사용과 같은 작업에 매우 유용합니다. 예를 들어 많은 LINQ 쿼리 Func<T,TResult>는 필터링에 사용할 수 있는 대리자를 사용합니다 (종종 ).



2

예를 들면 여기 에서 볼 수 있습니다 . 특정 요구 사항을 충족하는 개체를 처리하는 방법이 있습니다. 그러나 여러 방법으로 개체를 처리 할 수 ​​있기를 원합니다. 별도의 메서드를 만드는 대신 개체를 처리하는 일치 메서드를 대리자에게 할당하고 대리자를 개체를 선택하는 메서드에 전달하기 만하면됩니다. 이렇게하면 하나의 선택기 방법에 다른 방법을 할당 할 수 있습니다. 나는 이것을 쉽게 이해할 수 있도록 노력했다.


1

대리자를 사용하여 스레드와 통신합니다.

예를 들어 파일을 다운로드하는 Win Forms 앱이있을 수 있습니다. 앱은 다운로드를 수행하기 위해 작업자 스레드를 시작합니다 (GUI가 잠기는 것을 방지 함). 작업자 스레드는 델리게이트를 사용하여 상태 메시지 (예 : 다운로드 진행률)를 주 프로그램으로 다시 보내므로 GUI가 상태 표시 줄을 업데이트 할 수 있습니다.



0

사용의 첫 번째 줄은 Observer / Observable (이벤트) 패턴을 바꾸는 것입니다. 두 번째는 전략 패턴의 멋지고 우아한 버전입니다. 내가 생각하는 처음 두 가지보다 더 난해하지만 다양한 다른 용도를 모을 수 있습니다.


0

이벤트, 기타 anynch 작업


0

동작을 캡슐화하고 싶을 때마다 균일 한 방식으로 호출하십시오. 이벤트 핸들러, 콜백 함수 등. 인터페이스 및 캐스트를 사용하여 유사한 작업을 수행 할 수 있지만 때로는 동작이 유형 이나 객체에 반드시 연결되어 있지는 않습니다 . 때로는 캡슐화해야하는 동작이 있습니다.


0

게으른 매개 변수 초기화! 모든 이전 답변 (전략 패턴, 관찰자 ​​패턴 등) 외에도 델리게이트를 사용하면 매개 변수의 지연 초기화를 처리 할 수 ​​있습니다. 예를 들어, 꽤 많은 시간이 걸리고 특정 DownloadedObject를 반환하는 함수 Download ()가 있다고 가정합니다. 이 개체는 특정 조건에 따라 저장소에서 사용됩니다. 일반적으로 다음을 수행합니다.

storage.Store(conditions, Download(item))

그러나 대리자 (보다 정확하게는 람다)를 사용하면 저장소의 서명을 변경하여 Condition 및 Func <Item, DownloadedObject>를 수신하고 다음과 같이 사용하여 다음을 수행 할 수 있습니다.

storage.Store(conditions, (item) => Download(item))

따라서 저장소는 필요한 경우에만 대리인을 평가하고 조건에 따라 다운로드를 실행합니다.


사소한 점이지만 "더 정확하게는 람다"입니다. C # 2.0의 익명 메서드를 사용하여 동일한 작업을 수행 할 수 있습니다. 더 장황 할 수 있습니다. delegate (ItemType item) {[return] Download (item);}
Marc Gravell

물론 LINQ와 동일합니다. 람다는 대리자를위한 구문 설탕 일뿐입니다. 그들은 단지 델리게이트의 접근성을 높였습니다.
Santiago Palladino

람다는 대리자뿐만 아니라 표현식 트리로 변환 할 수 있기 때문에 단순한 대리자 그 이상입니다.
Jon Skeet

음, 람다는 델리게이트와 완전히 다른 표현식으로 컴파일 할 수도 있습니다 . 그러나 귀하의 예제에서는 Anon 메서드에서 사용할 수있는 Func <,>를 사용했습니다. 식은 C # 2.0으로 작성하는 것이 매우 힘들 것입니다.
Marc Gravell


0

In Array.Sort (T [] 배열, 비교 비교), List.Sort (Comparison 비교) 등의 비교 매개 변수


0

내가 아는 한 대리자는 함수 포인터로 변환 할 수 있습니다. 이것은 원래 프로그래머가 그러한 일이 일어나도록 어떤 준비도하지 않았음에도 불구하고 효과적으로 객체 지향적 일 수 있기 때문에 함수 포인터를 취하는 네이티브 코드와 상호 운용 할 때 삶을 훨씬 더 쉽게 만듭니다.


0

Delegate는 참조로 메서드를 호출하는 데 사용됩니다. 예를 들면 :

  delegate void del_(int no1,int no2);
class Math
{
   public static void add(int x,int y)
   {
     Console.WriteLine(x+y);
   }
   public static void sub(int x,int y)
   {
     Console.WriteLine(x-y);
   }
}



    class Program
    {
        static void Main(string[] args)
        {
            del_ d1 = new del_(Math.add);
            d1(10, 20);
            del_ d2 = new del_(Math.sub);
            d2(20, 10);
            Console.ReadKey();
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.