Wykorzystanie C # Delegatów w świecie rzeczywistym [zamknięte]

16

Myślę, że koncepcyjnie rozumiem delegatów C #, jednak staram się znaleźć przykład z prawdziwego świata, w którym byliby przydatni. Czy możesz podać odpowiedzi szczegółowo opisujące, w jaki sposób delegaci C # byli wykorzystywani w rzeczywistych aplikacjach i jakie problemy umożliwiły ci poruszanie się.

AlexC
źródło
2
Prawie każda klasa w .NET udostępnia pewien zestaw zdarzeń, więc proszę bardzo. To tylko sposób na zawarcie jakiejś jednostki pracy. Załóżmy na przykład, że implementujesz ogólną strukturę binarnego drzewa w C. Cóż, jedynym sposobem na uporządkowanie drzewa byłoby przyjęcie jako parametru wskaźnika funkcji, który wiedział, jak wykonać sortowanie.
Ed S.

Odpowiedzi:

16

Kod GUI używa delegatów do obsługi zdarzeń, takich jak kliknięcia przycisków, ruchy okien. Korzystanie z pełnomocnika pozwala mieć funkcję wywoływaną za każdym razem, gdy nastąpi zdarzenie. Przykładem może być połączenie funkcji, która zapisuje dane do przycisku „Zapisz” na interfejsie. Kliknięcie przycisku powoduje skonfigurowanie funkcji zapisywania danych. Jest to przydatne w programowaniu GUI, ponieważ cały program może czekać na użytkownika, aby coś zrobił, a ty nie możesz wiedzieć, co zrobi w pierwszej kolejności. Korzystanie z delegatów pozwala na połączenie funkcji twojego programu z interfejsem użytkownika w taki sposób, aby użytkownik mógł robić rzeczy w dowolny sposób.

FrustratedWithFormsDesigner
źródło
2
++ Masz rację, ale wciąż go nienawidzę :-) wieki temu wymyśliłem to .
Mike Dunlavey
12

Linq używa parametrów Func<T>i Action<T>deleguje w dowolnym miejscu jako parametry.

Pozwalają one używać wyrażeń lambda jako parametrów i definiują akcję, która ma być podjęta jako część listy parametrów.

Oded
źródło
12

Praktycznie wszystko przy użyciu Wzorca Obserwatora , prawdopodobnie implementuje delegatów.

Przeczytaj opis, a prawdopodobnie wyobrazisz sobie scenariusze, w których byś ich użył. Obsługa zdarzeń GUI jest częstym przykładem.

Jaka jest nazwa?
źródło
+1, wzorzec strategii jest naprawdę tam, gdzie świecą delegaci, tj. Masz klasę, w której jakaś metoda coś robi, ale chcesz, aby coś było wymienne i bez bezpośredniej zależności, ergo delegaci. Zauważ, że wydarzenia wypełniają tę samą potrzebę co delegaci, różnica polega na tym, że używasz delegatów, kiedy musisz zareagować na jakąś zwracaną wartość, podczas gdy po prostu odpalasz zdarzenia i cokolwiek będzie.
Homde
9

Delegaci są cholernie przydatni w programowaniu asynchronicznym.

Masz klasę, która robi rzeczy asynchronicznie i ma wywołanie zwrotne. Możesz wywołać metodę delegowaną po wywołaniu zwrotnym - a implementacja klasy wykona logikę opisaną w metodzie delegowanej.

James Love
źródło
9

Delegaci są szczególnie przydatni jako rozwiązanie dla dziury w środkowym wzorze . Zasadniczo istnieje wiele przypadków, w których chcesz zapakować unikalny zestaw instrukcji do wspólnej grupy instrukcji. Jest to szczególnie trudne, jeśli instrukcje przed i po unikatowym bicie muszą współdzielić stan. W przypadku delegatów możesz po prostu przekazać delegata do funkcji. Funkcja wykonuje bit przed, wykonuje delegata, a następnie wykonuje bit później.

Scott Whitlock
źródło
5

W „dawnych czasach” języków innych niż OOP, takich jak Fortran i C, niezwykle przydatne było, aby podprogram otrzymał argument, który był wskaźnikiem funkcji. Na przykład qsortfunkcja działa z podaną przez użytkownika funkcją porównawczą. Istnieje wiele podprogramów rozwiązywania zwykłych równań różniczkowych lub optymalizacji funkcji i wszystkie one przyjmują wskaźniki funkcji jako argumenty.

W systemach okienkowych wszystkie rodzaje wywołań zwrotnych mają ten sam wzór.

W Lisp, nawet we wczesnych dniach, istniało coś, co nazywano „argumentem funkcjonalnym” lub FUNARG, co było nie tylko funkcją, ale zawierało także kontekst pamięci, w którym mógł pamiętać i wchodzić w interakcje z częścią świata zewnętrznego.

Ta sama potrzeba istnieje w językach OOP, z wyjątkiem sytuacji, gdy przekazujesz adres funkcji, musisz także przekazać adres obiektu, którego funkcja jest metodą. To dwie rzeczy, które musisz przekazać. Tak więc delegat jest właśnie taki i pozwala na stosowanie tego starego dobrego wzorca.

Mike Dunlavey
źródło
3

Oto prosty przykład, który pokazuje, jak użyteczni mogą być delegaci w tworzeniu prostego kodu, który działa zgodnie z zasadą DRY. Pozwala także zachować kod bardzo blisko miejsca, w którym jest potrzebny.

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");

Oto rzeczywisty przykład korzyści zapewnianej przez delegatów.

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);    
        }
    }
}
ChaosPandion
źródło
2

Moje pierwsze spotkanie z delegatami polegało na sprawdzeniu aktualizacji programu (Windows tworzy C # 3.5) poprzez pobranie pliku z mojej strony internetowej, ale aby uniknąć sprawdzania aktualizacji blokującej cały program, użyłem delegata i wątku, aby zrobić to asynchronicznie.

Rozpoznać
źródło
1

Widziałem ciekawe wdrożenia wzorca strategii, który skutecznie wykorzystuje delegatów. (tzn. strategia jest delegatem)

Ten, na który patrzyłem, dotyczył wyszukiwania ścieżek, gdzie algorytm do znalezienia ścieżki był delegatem, któremu można (ponownie) przypisać w czasie wykonywania, aby można było użyć różnych algorytmów (BFS vs. A * itp.)

Steven Evers
źródło
1

Wiele klasycznych wzorców GoF można zaimplementować za pomocą delegatów: Na przykład, wzorzec polecenia, wzorzec gościa, wzorzec strategii, wzorzec fabryczny i wzorzec obserwatora można często zaimplementować za pomocą prostego delegata. Czasami klasa jest lepsza (np. Gdy polecenie wymaga nazwy lub obiekt strategii wymaga serializacji), ale w większości przypadków użycie Action<...>lub Func<...>jest o wiele bardziej eleganckie niż utworzenie dedykowanego interfejsu jednokierunkowego.

nikie
źródło