Os representantes não são apenas interfaces abreviadas?

Suponha que tenhamos:

interface Foo 
{
 bool Func(int x);
}

class Bar: Foo
{
  bool Func(int x)
  {
   return (x>0);
  }  
}

class Baz: Foo
{
  bool Func(int x)
  {
   return (x<0);
  }  
}

Agora podemos jogar Bar e Baz como Foos e chamar seus métodos Func.

Os delegados simplificam isso um pouco:

delegate bool Foo(int x);

bool Bar(int x)
{
 return (x<0);
}

bool Baz(int x)
{
 return (x>0);
}

Agora podemos jogar em torno de Bar e Baz como delegados de Foo.

Qual é o benefício real dos delegados, exceto para obter códigos mais curtos?

0
adicionado editado
Visualizações: 14

10 Respostas

Não, os delegados são para ponteiros de método. Em seguida, você pode verificar se a assinatura do método associado ao delegado está correta.

Além disso, você não precisa conhecer a estrutura da classe. Dessa forma, você pode usar um método que tenha escrito para passar para um método em outra classe e definir a funcionalidade que deseja que aconteça.

Take a look at the List<> class with the Find method. Now you get to define what determines if something is a match or not, without requiring items contained in the class to implement IListFindable or something similar.

0
adicionado

Um delegado é um ponteiro de método digitado. Isso lhe dá mais flexibilidade do que as interfaces, pois você pode tirar proveito da covariância e contravariância, e você pode modificar o estado do objeto (você teria que passar o ponteiro por volta com functores baseados na interface).

Além disso, os delegados têm muito açúcar sintático que permite que você faça coisas como combiná-los facilmente.

0
adicionado

Em pelo menos uma proposta para adicionar encerramentos (isto é, delegados anônimos) a Java, eles são equivalentes a interfaces com um método de único membro.

0
adicionado

Pode-se pensar em delegados como uma interface para um método que define quais argumentos e tipo de retorno um método deve ter para se ajustar ao delegado

0
adicionado

Um delegado compartilha muito em comum com uma referência de interface que possui um único método do ponto de vista do chamador.

No primeiro exemplo, Baz e Bar são classes, que podem ser herdadas e instanciadas. No segundo exemplo, Baz e Bar são métodos.

Você não pode aplicar referências de interface a qualquer classe que corresponda ao contrato de interface. A classe deve declarar explicitamente que suporta a interface. Você pode aplicar uma referência de delegado a qualquer método que corresponda à assinatura.

Você não pode incluir métodos estáticos no contrato de uma interface. (Embora você possa aplicar métodos estáticos em métodos de extensão). Você pode se referir a métodos estáticos com uma referência de delegado.

0
adicionado

Você pode passar delegados como parâmetros em funções (Ok, tecnicamente os delegados se tornam objetos quando compilados, mas esse não é o ponto aqui). Você poderia passar um objeto como um parâmetro (obviamente), mas então você está vinculando esse tipo de objeto à função como um parâmetro. Com delegados, você pode passar qualquer função para executar no código que tenha a mesma assinatura, independentemente de onde ela venha.

0
adicionado

Sim, um delegado pode ser considerado uma interface com um método.

0
adicionado

Existe uma pequena diferença, os delegados podem acessar as variáveis ​​de membros das classes nas quais elas são definidas. Em C# (ao contrário de Java), todas as classes internas são consideradas estáticas. Portanto, se você estiver usando uma interface para gerenciar um retorno de chamada, por exemplo um ActionListener para um botão. A classe interna de implementação precisa ser passada (por meio do construtor) para as partes da classe que pode precisar interagir durante o retorno de chamada. Delegados não têm essa restrição, portanto, reduz a quantidade de código necessária para implementar o retorno de chamada.

Um código mais curto e mais conciso também é um benefício digno.

0
adicionado
Não são apenas variáveis ​​de membro. São variáveis ​​ou parâmetros locais do método no qual um delegado anônimo é definido.
adicionado o autor Daniel Earwicker, fonte
@supercat Correto. Mas por que você acha que as interfaces "funcionariam melhor" do que os delegados? Um delegado é logicamente a mesma coisa que uma interface de método único.
adicionado o autor Daniel Earwicker, fonte
(1) "instâncias de heap" não são especialmente caras no CLR. Veja smellegantcode.wordpress. com/2009/03/01/& hellip; (2) O que há de errado com delegate void MyDelegate (T v) onde T: IFoo, IBar; ?
adicionado o autor Daniel Earwicker, fonte
adicionado o autor Daniel Earwicker, fonte
@DanielEarwicker: Uma rotina na qual um encerramento é definido define uma classe "oculta" especial para conter variáveis ​​locais, cria uma instância de tal classe sempre que essas variáveis ​​entram no escopo e usa os campos dessa classe em vez de locais "normais" variáveis. Cenários que usam closures funcionariam melhor com interfaces do que com delegados.
adicionado o autor supercat, fonte
@DanielEarwicker: Pelo menos dois motivos: (1) Suponha que uma rotina chamará uma Ação em alguma situação, e nessa situação eu quero chamar Foo (x) com o valor presente da variável local x . Passar tal solicitação como um delegado requer a criação de duas instâncias de heap: um objeto de classe temporário para conter x e um delegado para chamá-lo. Passar a solicitação como um tipo de interface exigiria a criação de uma instância de heap (uma implementação da interface). Passá-lo como um genérico com restrição de interface pode não exig
adicionado o autor supercat, fonte
(2) As interfaces podem ter métodos genéricos abertos, enquanto os delegados não podem. Suponha que eu tenha uma coleção de objetos sem nenhum tipo de base comum, mas todos são de tipos restritos para implementar IFoo e IBar. Eu gostaria de ser capaz de executar uma rotina fornecida externamente que leva um parâmetro restrito para IFoo e IBar. Não há como especificar que um delegado usa um parâmetro do tipo T: IFoo, IBar , mas é possível declarar um método de interface que faz isso.
adicionado o autor supercat, fonte
@DanielEarwicker: O problema é que a rotina que cria o delegado tem que especificar o tipo T . Suponha que uma classe tenha uma coleção de objetos que implementam I1 e I2 , mas eles não têm um tipo de base comum que implementa ambos; Deseja expor um método ForAll . Se tal método aceitasse um objeto do tipo interface IActUponConstrained {void Act (T param) onde T: I1, I2;} , poderia chamar Act < T> em cada item sem a entidade que criou o IActUponConstrained precisando conhe
adicionado o autor supercat, fonte
@DanielEarwicker: Se alguém não quiser usar o Reflection, a interface genérica é necessária porque os parâmetros de digitação genéricos só podem ser movidos "para baixo" (mais fundo) na pilha de chamadas. Uma coleção de classes abstratas ThingImplementing pode conter instâncias de classes derivadas ThingImplementing .WithType onde ActualType: I1, I2 . Cada instância é vinculada ao tipo genérico com o qual foi criada, mas se outro código for chamado usando esse parâmetro de tipo, a instância terá que fazer a própria chamada. Se
adicionado o autor supercat, fonte
... ambas as interfaces, não há como definir um delegado com um parâmetro de tipo restrito a I1 e I2 , ao qual um ThingImplementing .WithType poderia passar um Foo , e para o qual um ThingImplementing .WithType poderia passar uma barra . Pode-se, entretanto, definir uma implementação de IActUponConstrained , passá-la para os dois objetos e ter a primeira chamada Agir (Foo param) enquanto a segunda chama Aja (Bar param)
adicionado o autor supercat, fonte
@DanielEarwicker: Precisamente. A capacidade de fazer com que o consumidor de um tipo de interface forneça um parâmetro de tipo genérico é diferente de tudo o que pode ser feito com delegados .net. Na verdade, não tenho certeza de que haveria necessidade real de delegados no framework se compiladores pudessem fazer por interfaces genéricas algumas das coisas que eles fazem para delegados (por exemplo, dada uma interface com um Invoke método tendo uma certa assinatura, construa uma classe cujo construtor obtenha duas implementações dessa interface e que implemente chamando ambas
adicionado o autor supercat, fonte
... genéricos não existiam em .net 1.0, então delegados quase genéricos eram um substituto necessário. Aliás, eu me pergunto por que derivadas geradas pelo compilador de Delegate não definem seus próprios métodos Combine e Remove específicos do tipo? Esses métodos podem operar corretamente com tipos delegados contravariantes, enquanto Delegate.Combine não pode.
adicionado o autor supercat, fonte
É interessante notar que os delegados podem acessar os membros. Um método de classe envolvida a ser transmitido poderia facilmente agir como um mensageiro para os dados resultantes, mas o acesso direto é uma maneira muito mais rápida de lidar com isso.
adicionado o autor Rick Minerich, fonte

Do ponto de vista da Engenharia de Software, você tem razão, os delegados são muito parecidos com as interfaces de função, pois eles prototipam uma interface de função.

Eles também podem ser usados ​​da mesma maneira: ao invés de passar uma classe inteira contendo o método que você precisa, você pode passar apenas um delegado. Isso economiza muito código e cria um código muito mais legível.

Além disso, com o advento das expressões lambda, agora elas também podem ser facilmente definidas na mosca, o que é um grande bônus. Embora seja POSSÍVEL criar classes dinamicamente em C#, é realmente uma grande dor na bunda.

Comparar os dois é um conceito interessante. Eu não tinha considerado anteriormente o quanto as idéias são de um ponto de vista de caso de uso e estruturação de código.

0
adicionado

Interfaces e delegados são duas coisas totalmente diferentes, embora eu entenda a tentação de descrever os delegados em termos de interface para facilitar a compreensão ... no entanto, não saber a verdade pode levar a confusão.

Os delegados foram inspirados (em parte) por causa da arte negra dos ponteiros do método C ++ serem inadequados para certos propósitos. Um exemplo clássico é implementar um mecanismo de transmissão de mensagens ou de eventos. Delegados permitem que você defina uma assinatura de método sem qualquer conhecimento de tipos ou interfaces de uma classe - eu poderia definir um delegado "void eventHandler (Event * e)" e invocá-lo em qualquer classe que o implementasse.

Para obter algumas informações sobre esse problema clássico e por que os delegados são desejáveis, leia isto e então isto .

0
adicionado