Programming

언제 & 왜 대리자를 사용해야합니까?

procodes 2020. 3. 3. 23:09
반응형

언제 & 왜 대리자를 사용해야합니까? [복제]


이 질문에는 이미 답변이 있습니다.

나는 C #에서 비교적 새롭고 Delegates를 적절하게 사용할 때 궁금 합니다. 그것들은 이벤트 선언에 널리 사용되지만 언제 내 코드에서 사용해야 합니까? 왜 유용합니까? 왜 다른 것을 사용하지 않습니까?

또한 언제 대리자를 사용해야하는지 궁금 하며 다른 대안이 없습니다 .

도와 줘서 고마워!

편집 : 나는 여기 에 대리인 필요한 사용을 찾았습니다 생각


나는 이미 말한 모든 것에 동의합니다. 단지 다른 단어를 넣으려고합니다.

델리게이트는 a / some 메소드의 자리 표시 자로 볼 수 있습니다.

델리게이트를 정의하면 클래스 사용자에게 " 이 서명과 일치하는 모든 메소드를 델리게이트에 자유롭게 할당하면 내 델리게이트가 호출 될 때마다 호출 됩니다." 라고 말합니다.

일반적인 사용은 물론 이벤트입니다. 모든 OnEventX 는 사용자가 정의한 메소드에 위임 합니다.

대표단은에 제공하는 유용한 사용자 의 오브젝트 자신의 동작을 사용자 정의하는 몇 가지 능력을. 대부분의 경우 다른 방법을 사용하여 동일한 목적을 달성 할 수 있으며 위임을 작성 하도록 강요 받을 수 있다고 생각하지 않습니다 . 일부 상황에서는 작업을 수행하는 것이 가장 쉬운 방법입니다.


델리게이트는 메소드에 대한 참조입니다. 객체는 메소드, 생성자 또는 무엇이든 매개 변수로 쉽게 보낼 수 있지만 메소드는 조금 더 까다 롭습니다. 그러나 가끔씩 메서드를 다른 메서드에 매개 변수로 보내야 할 필요가 있다고 생각할 수 있습니다. 이때 델리게이트가 필요합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyLibrary;

namespace DelegateApp {

  /// <summary>
  /// A class to define a person
  /// </summary>
  public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
  }

  class Program {
    //Our delegate
    public delegate bool FilterDelegate(Person p);

    static void Main(string[] args) {

      //Create 4 Person objects
      Person p1 = new Person() { Name = "John", Age = 41 };
      Person p2 = new Person() { Name = "Jane", Age = 69 };
      Person p3 = new Person() { Name = "Jake", Age = 12 };
      Person p4 = new Person() { Name = "Jessie", Age = 25 };

      //Create a list of Person objects and fill it
      List<Person> people = new List<Person>() { p1, p2, p3, p4 };

      //Invoke DisplayPeople using appropriate delegate
      DisplayPeople("Children:", people, IsChild);
      DisplayPeople("Adults:", people, IsAdult);
      DisplayPeople("Seniors:", people, IsSenior);

      Console.Read();
    }

    /// <summary>
    /// A method to filter out the people you need
    /// </summary>
    /// <param name="people">A list of people</param>
    /// <param name="filter">A filter</param>
    /// <returns>A filtered list</returns>
    static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
      Console.WriteLine(title);

      foreach (Person p in people) {
        if (filter(p)) {
          Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
        }
      }

      Console.Write("\n\n");
    }

    //==========FILTERS===================
    static bool IsChild(Person p) {
      return p.Age < 18;
    }

    static bool IsAdult(Person p) {
      return p.Age >= 18;
    }

    static bool IsSenior(Person p) {
      return p.Age >= 65;
    }
  }
}

일정한 간격 [a, b]에 걸쳐 일부 실제 함수 f ( x ) 를 통합하는 프로 시저를 작성하려고한다고 가정하십시오 . 이것을하기 위해 3-Point Gaussian 방법을 사용한다고 가정 해 봅시다.

이상적으로는 다음과 같은 기능을 원합니다.

// 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals.
static double Gauss3(Integrand f, double a, double b, int n) {
  double res = 0;

  // compute result
  // ...

  return res;
}

따라서 우리는 임의 Integrandf를 전달 하고 닫힌 간격 동안 명확한 적분을 얻을 수 있습니다.

어떤 유형이어야 Integrand합니까?

대리인없이

대리자가 없으면 eval다음과 같이 선언 된 단일 메소드가있는 일종의 인터페이스가 필요 합니다.

// Interface describing real-valued functions of one variable.
interface Integrand {
  double eval(double x);
}

다음과 같이이 인터페이스를 구현하는 전체 클래스를 만들어야합니다.

// Some function
class MyFunc1 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// Some other function
class MyFunc2 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// etc

그런 다음 Gauss3 메소드에서이를 사용하려면 다음과 같이 호출해야합니다.

double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);

그리고 Gauss3는 다음과 같은 모습을 보여야합니다.

static double Gauss3(Integrand f, double a, double b, int n) {
  // Use the integrand passed in:
  f.eval(x);
}

따라서에있는 임의의 함수를 사용하려면 모든 작업을 수행해야합니다 Guass3.

대리인과

public delegate double Integrand(double x);

이제 우리는 그 프로토 타입을 준수하는 정적 (또는 정적) 함수를 정의 할 수 있습니다 :

class Program {
   public delegate double Integrand(double x);   
   // Define implementations to above delegate 
   // with similar input and output types
   static double MyFunc1(double x) { /* ... */ }
   static double MyFunc2(double x) { /* ... */ }
   // ... etc ...

   public static double Gauss3(Integrand f, ...) { 
      // Now just call the function naturally, no f.eval() stuff.
      double a = f(x); 
      // ...
   }

   // Let's use it
   static void Main() {
     // Just pass the function in naturally (well, its reference).
     double res = Gauss3(MyFunc1, a, b, n);
     double res = Gauss3(MyFunc2, a, b, n);    
   }
}

간단한 작업을 위해 인터페이스, 복잡한 .eval, 객체 인스턴스화, 사용법과 같은 간단한 함수 포인터가 없습니다.

물론 델리게이트는 단순한 함수 포인터 이상의 기능이지만 별도의 문제 (함수 체인 및 이벤트)입니다.


대리자는 전달하려는 코드 블록을 선언 할 때 매우 유용합니다. 예를 들어 일반 재시도 메커니즘을 사용하는 경우

가짜:

function Retry(Delegate func, int numberOfTimes)
    try
    {
       func.Invoke();
    }
    catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }

또는 Transform액션 이있는 함수와 같이 코드 블록에 대한 늦은 평가를 수행 하고 Transform 함수 내에서 채울지 여부 또는 그 내용 을 알 필요없이 평가할 수 BeforeTransform있는 AfterTransform액션 액션을 BeginTransform원하는 경우 변형해야합니다.

물론 이벤트 처리기를 만들 때도 마찬가지입니다. 지금 코드를 평가하고 싶지는 않지만 필요한 경우에만 이벤트를 발생시킬 때 호출 할 수있는 대리자를 등록하십시오.


대리인 개요

대리인은 다음과 같은 속성을 갖습니다.

  • 대리자는 C ++ 함수 포인터와 비슷하지만 형식이 안전합니다.
  • 대리자는 메서드를 매개 변수로 전달할 수 있습니다.
  • 대리자를 사용하여 콜백 메소드를 정의 할 수 있습니다.
  • 대리인은 서로 연결될 수 있습니다. 예를 들어, 단일 이벤트에서 여러 메소드를 호출 할 수 있습니다.
  • 메서드는 대리자 서명과 정확히 일치 할 필요가 없습니다. 자세한 내용은 공분산 및 대비 분산을 참조하십시오.
  • C # 버전 2.0에는 Anonymous Methods라는 개념이 도입되어 별도로 정의 된 방법 대신 코드 블록을 매개 변수로 전달할 수 있습니다.

방금 이것들을 둘러 보았습니다. 따라서 이미 설명이 있지만 예제를 공유 할 것입니다. 그러나 현재 볼 수있는 한 가지 이점은 각각을 참조하는 2 개의 프로젝트를 가질 수없는 Circular Reference 스타일 경고를 해결하는 것입니다 다른.

응용 프로그램이 XML을 다운로드 한 다음 XML을 데이터베이스에 저장한다고 가정 해 봅시다.

여기에 솔루션을 빌드하는 두 가지 프로젝트가 있습니다 : FTP 및 SaveDatabase.

따라서 응용 프로그램은 다운로드를 찾아 파일을 다운로드하여 시작한 다음 SaveDatabase 프로젝트를 호출합니다.

이제 응용 프로그램은 메타 데이터가 포함 된 파일을 업로드하여 파일이 데이터베이스에 저장 될 때 FTP 사이트에 알려야합니다 (FTP 사이트 소유자의 요청은 무시). 문제는 어떤 시점과 방법에 있습니까? NotifyFtpComplete ()라는 새로운 메소드가 필요하지만 FTP 또는 SaveDatabase 중 어느 프로젝트에도 저장해야합니까? 논리적으로 코드는 FTP 프로젝트에 있어야합니다. 그러나 이는 NotifyFtpComplete가 트리거되거나 저장이 완료 될 때까지 기다린 다음 데이터베이스가 있는지 확인해야 함을 의미합니다. 우리가해야 할 일은 NotifyFtpComplete () 메소드를 직접 호출하도록 SaveDatabase 프로젝트에 알리는 것입니다. 우리는 ciruclar 참조를 얻을 것이고 NotifyFtpComplete ()는 개인 메소드입니다. 부끄러운 일이지만, 이것은 효과가 있었을 것입니다. 글쎄요

애플리케이션 코드 중에 메소드간에 매개 변수를 전달했지만 해당 매개 변수 중 하나가 NotifyFtpComplete 메소드 인 경우에는 어떻게됩니까? 예, 모든 코드가 포함 된 메소드를 전달합니다. 이것은 언제 어디서나 프로젝트에서 메소드를 실행할 수 있음을 의미합니다. 자, 이것이 대의원입니다. 즉, NotifyFtpComplete () 메서드를 매개 변수로 SaveDatabase () 클래스에 전달할 수 있습니다. 그것이 저장되는 시점에서 단순히 델리게이트를 실행합니다.

이 원유 예제가 도움이되는지 확인하십시오 (의사 코드). 또한 응용 프로그램이 FTP 클래스의 Begin () 메서드로 시작한다고 가정합니다.

class FTP
{
    public void Begin()
    {
        string filePath = DownloadFileFromFtpAndReturnPathName();

        SaveDatabase sd = new SaveDatabase();
        sd.Begin(filePath, NotifyFtpComplete());
    }

    private void NotifyFtpComplete()
    {
        //Code to send file to FTP site
    }
}


class SaveDatabase
{
    private void Begin(string filePath, delegateType NotifyJobComplete())
    {
        SaveToTheDatabase(filePath);

        /* InvokeTheDelegate - 
         * here we can execute the NotifyJobComplete
         * method at our preferred moment in the application,
         * despite the method being private and belonging
         * to a different class.
         */
        NotifyJobComplete.Invoke();
    }
}

따라서 설명을 통해 C #을 사용 하여이 콘솔 응용 프로그램으로 실제로 할 수 있습니다.

using System;

namespace ConsoleApplication1
{
    /* I've made this class private to demonstrate that 
    * the SaveToDatabase cannot have any knowledge of this Program class.
    */
    class Program
    {
        static void Main(string[] args)
        {
            //Note, this NotifyDelegate type is defined in the SaveToDatabase project
            NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);

            SaveToDatabase sd = new SaveToDatabase();            
            sd.Start(nofityDelegate);
            Console.ReadKey();
        }

        /* this is the method which will be delegated -
         * the only thing it has in common with the NofityDelegate
         * is that it takes 0 parameters and that it returns void.
         * However, it is these 2 which are essential.
         * It is really important to notice that it writes
         * a variable which, due to no constructor,
         * has not yet been called (so _notice is not initialized yet).
         */ 
    private static void NotifyIfComplete()
    {
        Console.WriteLine(_notice);
    }

    private static string _notice = "Notified";
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegate nd)
        {
            /* I shouldn't write to the console from here, 
             * just for demonstration purposes
             */
            Console.WriteLine("SaveToDatabase Complete");
            Console.WriteLine(" ");
            nd.Invoke();
        }
    }
    public delegate void NotifyDelegate();
}

코드를 단계별로 살펴보고 _notice가 호출되는 시점과 메소드 (델리게이트)가 호출되는 시점을 확인하면 문제가 명확 해지기를 바랍니다.

그러나 마지막으로 매개 변수를 포함하도록 델리게이트 유형을 변경하여 더 유용하게 만들 수 있습니다.

using System.Text;

namespace ConsoleApplication1
{
    /* I've made this class private to demonstrate that the SaveToDatabase
     * cannot have any knowledge of this Program class.
     */
    class Program
    {
        static void Main(string[] args)
        {
            SaveToDatabase sd = new SaveToDatabase();
            /* Please note, that although NotifyIfComplete()
         * takes a string parameter, we do not declare it,
         * all we want to do is tell C# where the method is
         * so it can be referenced later,
         * we will pass the parameter later.
         */
            var notifyDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(notifyDelegateWithMessage );

            Console.ReadKey();
        }

        private static void NotifyIfComplete(string message)
        {
            Console.WriteLine(message);
        }
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegateWithMessage nd)
        {
                        /* To simulate a saving fail or success, I'm just going
         * to check the current time (well, the seconds) and
         * store the value as variable.
         */
            string message = string.Empty;
            if (DateTime.Now.Second > 30)
                message = "Saved";
            else
                message = "Failed";

            //It is at this point we pass the parameter to our method.
            nd.Invoke(message);
        }
    }

    public delegate void NotifyDelegateWithMessage(string message);
}

대리인은 익명 인터페이스 라고 생각 합니다. 대부분의 경우 단일 메소드가있는 인터페이스가 필요할 때마다 사용할 수 있지만 해당 인터페이스를 정의하는 오버 헤드는 원하지 않습니다.


델리게이트는 특정 서명을 가진 메소드를 가리키고 본질적으로 타입 안전 함수 포인터가되는 간단한 클래스입니다. 델리게이트의 목적은 다른 메소드 (들)가 완료된 후 구조화 된 방식으로 콜백을 용이하게하는 것입니다.

이 기능을 수행하기 위해 광범위한 코드 세트를 생성 할 수도 있지만 그럴 필요는 없습니다. 대리인을 사용할 수 있습니다.

대리인을 만드는 것은 쉽습니다. "delegate"키워드를 사용하여 클래스를 델리게이트로 식별하십시오. 그런 다음 유형의 서명을 지정하십시오.

참고 URL : https://stackoverflow.com/questions/2019402/when-why-to-use-delegates



반응형