C # 대의원의 실제 사용


16

나는 C # 대리자를 개념적으로 이해한다고 생각하지만, 그들이 유용한 실제 사례를 찾는 데 어려움을 겪고 있습니다. 실제 응용 프로그램에서 C # 대리자가 사용 된 방식과 문제를 해결할 수있는 문제에 대해 자세히 설명 할 수 있습니까?


2
.NET 프레임 워크의 거의 모든 단일 클래스는 일부 이벤트 세트를 노출하므로 계속 진행할 수 있습니다. 작업 단위를 캡슐화하는 방법 일뿐입니다. 예를 들어 C에서 일반 이진 트리 구조를 구현한다고 가정하면 트리를 정렬하는 유일한 방법은 정렬 방법을 알고있는 함수 포인터를 매개 변수로 사용하는 것입니다.
Ed S.

답변:


16

GUI 코드는 델리게이트를 사용하여 버튼 클릭, 창 이동과 같은 이벤트를 처리합니다. 델리게이트를 사용하면 이벤트가 발생할 때마다 호출되는 함수를 가질 수 있습니다. 예를 들어 인터페이스의 "저장"버튼에 데이터를 저장하는 기능을 연결하는 것이 있습니다. 버튼을 클릭하면 데이터를 저장하는 기능을 실행하도록 설정됩니다. 전체 프로그램이 사용자가 무언가를 할 때까지 기다릴 수 있고 GUI가 무엇을 먼저 할 것인지 알 수 없기 때문에 GUI 프로그래밍에 유용합니다. 델리게이트를 사용하면 사용자가 원하는 방식으로 작업을 수행 할 수있는 방식으로 프로그램의 기능을 UI에 연결할 수 있습니다.


2
++ 당신 말이 맞지만, 나는 여전히 그것을 싫어합니다 :-) 그래서 오래 전에 나는 이것을 대신 생각해 냈습니다 .
Mike Dunlavey가

12

Linq는 모든 곳 에서 Func<T>Action<T>위임을 매개 변수로 사용합니다.

이를 통해 람다 식을 매개 변수로 사용하고 매개 변수 목록의 일부로 수행 할 작업을 정의 할 수 있습니다.


12

관찰자 패턴을 사용하는 거의 모든 것이 대의원을 구현할 것입니다.

설명을 읽으면 사용할 시나리오를 상상할 수 있습니다. GUI 이벤트 처리가 일반적인 예입니다.


+1, 전략 패턴은 실제로 델리게이트가 빛을 발하는 곳입니다. 즉, 어떤 메소드가 무언가를 수행하는 클래스가 있지만, 어떤 것이 상호 교환 가능하고 직접적인 의존성없이 ergo 델리게이트를 원합니다. 이벤트는 델리게이트와 같은 요구를 충족시키는 점에 유의하십시오. 차이점은 반환 값에 반응해야 할 때 델리게이트를 사용한다는 것입니다. 반면 이벤트를 시작하면 무엇이든지 될 것입니다.
Homde

9

대리인은 비동기식 프로그래밍에 유용합니다.

비동기 적으로 작업하고 콜백이있는 클래스가 있습니다. 콜백시 delegate 메소드를 호출 할 수 있으며 클래스 구현은 delegate 메소드에 설명 된 논리를 수행합니다.


9

대리인은 중간 패턴구멍에 대한 솔루션으로 특히 유용합니다 . 기본적으로 공통된 명령 모음 안에 고유 한 명령 집합을 래핑하려는 경우가 많습니다. 고유 비트 전후의 명령어가 상태를 공유해야하는 경우 특히 어렵습니다. 델리게이트를 사용하면 델리게이트를 함수에 전달할 수 있습니다. 이 함수는 이전 비트를 실행하고 델리게이트를 실행 한 후 이후 비트를 실행합니다.


5

Fortran 및 C와 같은 비 OOP 언어의 "오래된 날"에는 서브 루틴이 함수에 대한 포인터 인 인수를 수신하도록하는 것이 매우 유용했습니다. 예를 qsort들어이 기능은 사용자 제공 비교 기능과 함께 작동합니다. 일반적인 미분 방정식을 풀거나 함수를 최적화하기위한 수많은 서브 루틴이 있으며, 모두 함수 포인터를 인수로 사용합니다.

윈도우 시스템에서 모든 종류의 콜백은 동일한 패턴을 따릅니다.

Lisp에는 초기에도 "기능적 논증"또는 FUNARG라는 것이 있었는데, 이는 기능 일뿐 아니라 외부 세계의 일부를 기억하고 상호 작용할 수있는 스토리지 컨텍스트도 포함했습니다.

함수의 주소를 전달할 때 함수의 메소드 인 객체의 주소도 전달해야한다는 점을 제외하고는 OOP 언어에서도 이와 같은 요구가 존재합니다. 그것은 당신이 통과해야 할 두 가지입니다. 따라서 델리게이트는 바로 그 것이며 오래된 패턴을 계속 사용할 수 있습니다.


3

다음은 DRY 원칙을 따르는 간단한 코드를 만들 때 델리게이트가 얼마나 유용한 지 보여주는 간단한 예입니다. 또한 코드를 필요한 곳에 매우 가깝게 유지할 수 있습니다.

Action<Button, Action<Button>> prepareButton = 
    (btn, nxt) => { 
        btn.Height = 32;
        btn.Width= 64;
        nxt(btn);
    };

prepareButton(myBtn1, btn => btn.Text = "A");
prepareButton(myBtn2, btn => btn.Text = "B");
prepareButton(myBtn3, btn => btn.Text = "C");

다음은 대의원이 제공하는 이점의 실제 예입니다.

protected override void PageInitialize()
{
    const string selectCodeFormat = "javascript:selectCode('{0}', '{1}');";
    const string onClick = "return toggleElement(this);";

    Func<HtmlGenericControl> getElement = null;
    Action<HtmlGenericControl> setElement = null, addChild = null;
    HtmlGenericControl level1Element = null, level2Element = null, level3Element = null, level4Element = null;
    string className = null, code = null, description = null;           

    using (var records = Core.Database.ExecuteRecords("code.SocCodeTree"))
    {
        while (records.Read())
        {
            code = records.GetString("Code");
            description = records.GetString("Description"); 

            if (records.GetString("Level4") != "")
            {
                className = "Level4";
                setElement = e => level4Element = e;
                getElement = () => level4Element;
                addChild = e => level3Element.Controls.Add(e);
            }
            else if (records.GetString("Level3") != "")
            {
                className = "Level3";
                setElement = e => level3Element = e;
                getElement = () => level3Element;
                addChild = e => level2Element.Controls.Add(e);
            }
            else if (records.GetString("Level2") != "")
            {
                className = "Level2";
                setElement = e => level2Element = e;
                getElement = () => level2Element;
                addChild = e => level1Element.Controls.Add(e);
            }
            else
            {
                className = "Level1";
                setElement = e => level1Element = e;
                getElement = () => level1Element;
                addChild = e => Root.Controls.Add(e);
            }

            var child = new HtmlGenericControl("li");
            child.Attributes["class"] = className;
            var span = new HtmlGenericControl("span") { 
                InnerText = code + " - " + description + " - " 
            };
            span.Attributes["onclick"] = onClick;
            child.Controls.Add(span);
            var a = new HtmlAnchor() { 
                InnerText = "Select", 
                HRef = string.Format(selectCodeFormat, code, description) 
            };
            child.Controls.Add(a);
            setElement(new HtmlGenericControl("ul"));
            child.Controls.Add(getElement());
            addChild(child);    
        }
    }
}

2

대리인과의 첫 만남은 내 웹 사이트에서 파일을 다운로드하여 프로그램 업데이트 (Windows 양식 C # 3.5)를 확인하는 것이었지만 전체 프로그램을 잠그는 업데이트 확인을 피하기 위해 대리인과 스레드를 비동기 적으로 사용했습니다.


1

델리게이트를 효과적으로 사용하는 전략 패턴의 흥미로운 구현을 보았습니다. (예 : 전략은 대리인입니다)

내가보고있는 것은 경로를 찾는 알고리즘이 런타임에 (재) 할당되어 다른 알고리즘을 사용할 수있는 대리자 (BFS vs A * 등) 인 경로 찾기였습니다.


1

예를 들어, 명령 패턴, 방문자 패턴, 전략 패턴, 팩토리 패턴 및 관찰자 패턴은 종종 간단한 위임으로 구현할 수있는 많은 클래식 GoF 패턴을 델리게이트로 구현할 수 있습니다. 때로는 클래스가 더 낫습니다 (예 : 명령에 이름이 필요하거나 전략 개체를 직렬화해야하는 경우). 대부분의 경우 전용 1- 메소드 인터페이스를 만드는 것보다 사용 Action<...>하거나 Func<...>훨씬 우아합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.