딜러를 사용해야 하는 시기와 이유는 무엇입니까?
저는 C#에 비교적 익숙하지 않아서 언제 딜러를 적절하게 사용해야 하는지 궁금합니다.그것들은 이벤트 선언에 널리 사용되지만, 언제 내 코드에서 그것들을 사용해야 하고 왜 그것들이 유용한가요?왜 다른 것을 사용하지 않습니까?
또한 언제 대리인을 사용해야 하는지 궁금하고 다른 대안이 없습니다.
도와주셔서 감사합니다!
편집: 딜러가 필요로 하는 용도를 찾은 것 같습니다.
대리자는 메서드에 대한 참조입니다.객체는 메소드, 컨스트럭터 등으로 매개변수로 쉽게 전송될 수 있지만 메소드는 좀 더 까다롭습니다.그러나 때때로 다른 메소드에 메소드를 매개 변수로 보내야 할 필요성을 느낄 수 있으며, 이때 대리인이 필요합니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
}
}
}
출력:
Children:
Jake, 12 years old
Adults:
John, 41 years old
Jane, 69 years old
Jessie, 25 years old
Seniors:
Jane, 69 years old
저는 이미 언급된 모든 것에 동의합니다. 단지 다른 말을 덧붙이려고 노력할 뿐입니다.
대리자는 일부 메서드에 대한 자리 표시자로 볼 수 있습니다.
대리자를 정의하면 클래스 사용자에게 "이 서명과 일치하는 메서드를 대리자에게 할당하십시오. 그러면 대리자가 호출될 때마다 호출됩니다."라고 말합니다.
일반적으로 사용되는 것은 물론 이벤트입니다.모든 OnEventX는 사용자가 정의한 메서드에 위임합니다.
딜러는 개체의 사용자에게 개체의 동작을 사용자 지정할 수 있는 몇 가지 기능을 제공하는 데 유용합니다.대부분의 경우 동일한 목적을 달성하기 위해 다른 방법을 사용할 수 있으며, 저는 당신이 대의원을 만들도록 강요받을 수 있다고 생각하지 않습니다.그것은 어떤 상황에서는 일을 끝내는 가장 쉬운 방법입니다.
어떤 간격[a, b]에 걸쳐 일부 실제 값 함수 f(x)를 적분하는 절차를 작성하려고 한다고 가정합니다.이를 위해 3점 가우스 방법을 사용한다고 가정합니다(물론 어떤 방법으로도 가능합니다).
이상적으로는 다음과 같은 기능이 필요합니다.
// '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;
}
그래서 우리는 어떤 것이든 전달할 수 있습니다.Integrand
f, 닫힌 구간에 대한 확실한 적분을 구합니다.
요?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
액션, 그리고 갖고 싶은 것.BeforeTransform
리고그.AfterTransform
함수 할 수 입니다.BeginTransform
채워졌거나 변환해야 하는 항목입니다.
물론 이벤트 핸들러를 만들 때도 마찬가지입니다.지금은 코드를 평가하지 않고 필요할 때만 평가하려고 하므로 이벤트가 발생할 때 호출할 수 있는 대리자를 등록합니다.
딜러 개요
딜러의 속성은 다음과 같습니다.
- 딜러는 C++ 함수 포인터와 비슷하지만 유형은 안전합니다.
- 대리인은 메소드를 매개 변수로 전달할 수 있습니다.
- 딜러는 콜백 방법을 정의하는 데 사용할 수 있습니다.
- 예를 들어, 단일 이벤트에 대해 여러 메서드를 호출할 수 있습니다.
- 메서드가 위임 서명과 정확하게 일치할 필요는 없습니다.자세한 내용은 공분산 및 대비 분산을 참조하십시오.
- C# 버전 2.0에는 별도로 정의된 메서드 대신 코드 블록을 매개 변수로 전달할 수 있는 Anonymous Methods의 개념이 도입되었습니다.
여기서는 이미 설명이 있으므로 예를 들어 설명하겠지만, 현재로서는 두 개의 프로젝트가 서로 참조할 수 없는 Circular Reference 스타일 경고를 피할 수 있다는 것이 장점입니다.
응용프로그램이 XML을 다운로드한 다음 XML을 데이터베이스에 저장한다고 가정합니다.
여기에 제 솔루션을 구축하는 두 가지 프로젝트가 있습니다. FTP와 Save Database입니다.
따라서 응용프로그램은 다운로드를 검색하고 파일을 다운로드한 다음 데이터베이스 저장 프로젝트를 호출합니다.
이제 우리 애플리케이션은 메타데이터가 포함된 파일을 업로드하여 파일이 데이터베이스에 저장될 때 FTP 사이트에 알려야 합니다(이유는 무시하고 FTP 사이트 소유자의 요청입니다).문제는 어느 시점에서 어떻게 발생합니까?NotifyFtpComplete()라는 새 메서드가 필요하지만 FTP와 SaveDatabase 중 어떤 프로젝트에도 저장해야 합니까?논리적으로, 그 코드는 우리의 FTP 프로젝트에 있어야 합니다.그러나 이는 NotifyFtpComplete를 트리거하거나 저장이 완료될 때까지 기다린 다음 데이터베이스에 있는지 쿼리해야 함을 의미합니다.우리가 해야 할 일은 데이터베이스 저장 프로젝트에 NotifyFtpComplete() 메서드를 직접 호출하도록 지시하는 것입니다. 원형 참조가 제공되며 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();
}
저는 당신이 코드를 단계적으로 살펴보고 _통지가 언제 호출되는지, 그리고 메서드(대리인)가 언제 호출되는지 확인하는 것이 매우 명확하기를 바랍니다.
그러나 마지막으로 매개 변수를 포함하도록 위임 유형을 변경하여 더 유용하게 사용할 수 있습니다.
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);
}
저는 대리인을 익명 인터페이스로 간주합니다.대부분의 경우 단일 메소드를 사용하여 인터페이스가 필요할 때마다 이러한 메소드를 사용할 수 있지만 해당 인터페이스를 정의하는 오버헤드는 발생하지 않습니다.
대리자는 특정 서명이 있는 메서드를 가리키는 데 사용되는 단순 클래스로, 기본적으로 유형 안전 함수 포인터가 됩니다.딜러의 목적은 다른 메소드(또는 메소드)가 완료된 후 구조화된 방식으로 다른 메소드(또는 메소드)에 대한 콜백을 용이하게 하는 것입니다.
이 기능을 수행하기 위해 광범위한 코드 집합을 만들 수 있지만, 그럴 필요는 없습니다.대리인을 사용할 수 있습니다.
대리인을 만드는 것은 쉽습니다.딜러 키워드를 사용하여 클래스를 딜러로 식별합니다.그런 다음 형식의 서명을 지정합니다.
언급URL : https://stackoverflow.com/questions/2019402/when-why-to-use-delegates
'programing' 카테고리의 다른 글
WPF 사용자 제어 상위 항목 (0) | 2023.05.06 |
---|---|
iOS 프로젝트를 빌드하지 못했습니다."xcodebuild" 명령을 실행했지만 오류 코드 65와 함께 종료되었습니다. (0) | 2023.05.06 |
SQL Server에서 외부 키 종속성을 찾는 방법은 무엇입니까? (0) | 2023.05.06 |
StoryBoard ID란 무엇이며 어떻게 사용할 수 있습니까? (0) | 2023.05.06 |
NSURL을 로컬 파일 경로로 변환 (0) | 2023.05.06 |