Uso no mundo real de delegados C # [fechado]

16

Penso que compreendo conceitualmente os delegados em C #, no entanto, estou lutando para encontrar um exemplo do mundo real onde eles seriam úteis. Você pode fornecer algumas respostas detalhando como os representantes do C # foram usados ​​em aplicativos reais e quais problemas eles permitiram que você contornasse.

AlexC
fonte
2
Quase todas as classes da estrutura .NET expõem algum conjunto de eventos, então pronto. É apenas uma maneira de encapsular alguma unidade de trabalho. Por exemplo, digamos que você estivesse implementando uma estrutura de árvore binária genérica em C. Bem, a única maneira de ordenar sua árvore seria tomar como parâmetro um ponteiro de função que soubesse fazer a classificação.
Ed S.

Respostas:

16

O código da GUI usa delegados para manipular eventos, como cliques no botão, movimentos da janela. O uso do delegado permite que você tenha uma função chamada sempre que o evento ocorrer. Um exemplo seria vincular uma função que salva dados a um botão "Salvar" na interface. Quando o botão é clicado, ele é configurado para executar a função que salva os dados. É útil na programação da GUI, porque todo o seu programa pode estar esperando o usuário fazer algo e você não tem como saber o que ele fará primeiro. O uso de delegados permite que a funcionalidade do seu programa seja conectada à interface do usuário de forma que o usuário possa fazer as coisas da maneira que desejar.

FrustratedWithFormsDesigner
fonte
2
++ Você está certo, mas eu ainda odeio :-) então, eras atrás, eu vim com isso .
Mike Dunlavey
12

O Linq usa Func<T>e Action<T>delega em todo o lugar como parâmetros.

Isso permite que você use expressões lambda como parâmetros e defina a ação a ser tomada como parte da lista de parâmetros.

Oded
fonte
12

Praticamente qualquer coisa usando o Padrão Observador provavelmente implementaria delegados.

Leia a descrição e você provavelmente imaginará alguns cenários em que os usaria. A manipulação de eventos da GUI é um exemplo comum.

whatsisname
fonte
+1, o padrão de estratégia é realmente onde os delegados brilham, ou seja, você tem alguma classe em que algum método faz alguma coisa, mas deseja que algo seja intercambiável e sem dependência direta, portanto, os delegados. Observe que os eventos meio que preenchem a mesma necessidade dos delegados, a diferença é que você os usa quando precisa reagir com algum valor de retorno, enquanto você apenas aciona eventos e o que quer que seja.
Homde 28/02
9

Os delegados são muito úteis na programação assíncrona.

Você tem uma classe que faz coisas de forma assíncrona e tem um retorno de chamada. Você pode ter o método delegado invocado no retorno de chamada - e sua implementação de classe fará a lógica descrita no seu método delegado.

James Love
fonte
9

Delegados são particularmente úteis como uma solução para o buraco no meio do teste padrão . Essencialmente, existem muitos casos em que você deseja agrupar um conjunto exclusivo de instruções dentro de um conjunto comum de instruções. É particularmente difícil se as instruções antes e depois do bit único precisarem compartilhar o estado. Com delegados, você pode simplesmente passar um delegado para uma função. A função executa o bit anterior, executa o delegado e depois executa o bit posterior.

Scott Whitlock
fonte
5

Nos "velhos tempos" de linguagens não OOP, como Fortran e C, era incrivelmente útil poder fazer com que uma sub-rotina recebesse um argumento que fosse um ponteiro para uma função. Por exemplo, a qsortfunção funciona com uma função de comparação fornecida pelo usuário. Existem inúmeras sub-rotinas para resolver equações diferenciais ordinárias ou para otimizar funções, e todas elas usam ponteiros de função como argumentos.

Nos sistemas de janelas, todos os tipos de retornos de chamada seguem o mesmo padrão.

No Lisp, mesmo nos primeiros dias, havia algo chamado "argumento funcional" ou FUNARG, que não era apenas uma função, mas também continha um contexto de armazenamento em que podia se lembrar e interagir com parte do mundo externo.

Essa mesma necessidade existe nos idiomas OOP, exceto quando você passa o endereço de uma função, também precisa passar o endereço do objeto do qual a função é um método. São duas coisas que você precisa passar. Portanto, um delegado é exatamente isso e permite que esse bom e velho padrão ainda seja usado.

Mike Dunlavey
fonte
3

Aqui está um exemplo simples que mostra como os delegados podem ser úteis na criação de código simples que segue o princípio DRY. Também permite que você mantenha o código extremamente perto de onde é necessário.

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

Aqui está um exemplo do mundo real da vantagem que os delegados fornecem.

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
fonte
2

Meu primeiro encontro com delegados foi procurar uma atualização do programa (Windows Forms C # 3.5) baixando um arquivo do meu site, mas para evitar a verificação da atualização bloqueando todo o programa, usei um delegado e um thread para fazê-lo de forma assíncrona.

Ken
fonte
1

Vi implementações interessantes do padrão de estratégia que usa delegados de maneira eficaz. (ou seja, a estratégia é um delegado)

O que eu estava olhando era para encontrar o caminho onde o algoritmo para encontrar o caminho era um delegado que poderia ser (re) atribuído em tempo de execução para que diferentes algoritmos pudessem ser usados ​​(BFS vs A * etc.)

Steven Evers
fonte
1

Muitos dos padrões clássicos do GoF podem ser implementados com delegados: Por exemplo, padrão de comando, padrão de visitante, padrão de estratégia, padrão de fábrica e padrão de observador geralmente podem ser implementados com um simples delegado. Às vezes, uma classe é melhor (por exemplo, quando um comando precisa de um nome ou um objeto de estratégia precisa ser serializado), mas na maioria dos casos, o uso Action<...>ou Func<...>é muito mais elegante do que a criação de uma interface de método único dedicada.

nikie
fonte