Resumo : neste tutorial, você aprenderá sobre os delegados C# e como usá-los de maneira eficaz.
Introdução aos delegados C#
Em C#, delegados são tipos que representam referências a métodos com uma lista de parâmetros e tipo de retorno específicos.
Para definir um delegado, você usa a delegate
palavra-chave e especifica a assinatura do método. Por exemplo:
delegate void Greeting(string message);
Linguagem de código: C# ( cs )
Neste exemplo, definimos o Greeting
tipo delegado que pode fazer referência a qualquer método que aceite um argumento de string e retorne void
.
Como the Greeting
é um tipo delegado, você pode declará-lo fora de uma classe como outras classes.
O seguinte define o SayHi()
método da Program
classe, que possui a mesma assinatura do Greeting
delegado:
class Program
{
static void SayHi(string name)
{
Console.WriteLine($"Hi {name}");
}
// ...
}
Linguagem de código: C# ( cs )
Para chamar o SayHi()
método por meio do Greeting
delegado, você cria uma instância do Greeting
delegado com o SayHi
método como argumento e chama o Invoke()
método da instância do delegado assim:
Greeting greeting = new Greeting(SayHi);
greeting.Invoke("John");
Linguagem de código: C# ( cs )
Nesta sintaxe, greeting
é uma instância do Greeting
tipo delegado. O greeting
delegado contém uma referência ao SayHi()
método. Internamente, o greeting
delegado mantém uma lista de invocação que contém uma referência ao SayHi()
método.
Quando você chama o Invoke()
método do greeting
delegado, C# chamará o SayHi()
método com o mesmo argumento. Portanto, as seguintes afirmações são funcionalmente equivalentes:
greeting.Invoke("John");
Linguagem de código: JavaScript ( javascript )
E:
SayHi("John");
Linguagem de código: JavaScript ( javascript )
C# fornece uma maneira mais curta de criar uma nova instância do Greeting
delegado, atribuindo o SayHi
método a uma variável delegada e chamando o método referenciado por meio do delegado:
Greeting greeting = SayHi;
greeting("John");
Linguagem de código: JavaScript ( javascript )
Observe que você atribui o nome do método SayHi
sem parênteses ()
à variável delegada.
Junte tudo.
delegate void Greeting(string message);
class Program
{
static void SayHi(string name)
{
Console.WriteLine($"Hi {name}");
}
static void Main(string[] args)
{
Greeting greeting = SayHi;
greeting("John");
}
}
Linguagem de código: C# ( cs )
Saída:
Hi John
Linguagem de código: C# ( cs )
Se você tem experiência em C++, a maneira mais rápida de entender os delegados é considerá-los como ponteiros de função. No entanto, um delegado é totalmente orientado a objetos em C#. E, diferentemente dos ponteiros de função C++, os delegados encapsulam uma instância de objeto e um método.
Por que delegados
Como você pode chamar o SayHi()
método diretamente, não precisa chamá-lo por meio do delegado. A questão é por que você precisa de um delegado?
Como os delegados mantêm referências a métodos, você pode passar métodos como argumentos para outros métodos por meio dos delegados. Portanto, os delegados são ideais para definir métodos de retorno de chamada .
Suponha que você queira definir um método que filtre uma lista de inteiros com base no resultado de outro método. Para fazer isso, você pode usar um delegado.
Primeiro, defina um tipo delegado que aceite um número inteiro e retorne um valor booleano:
delegate bool Callback(int x);
Linguagem de código: C# ( cs )
Segundo, defina o Filter()
método que aceita uma lista de números inteiros e uma instância do arquivo Callback
. Se um número inteiro fizer com que o retorno de chamada retorne true
, o resultado do Filter()
método incluirá esse número inteiro:
static List<int> Filter(List<int> numbers, Callback callback)
{
var results = new List<int>();
foreach (var number in numbers)
{
if (callback(number))
{
results.Add(number);
}
}
return results;
}
Linguagem de código: C# ( cs )
Terceiro, defina o isOdd()
método que retorna true
se um número for ímpar e o isEven()
método que retorna true
se um número for par:
static bool IsOdd(int x) => x % 2 != 0;
static bool IsEven(int x) => x % 2 == 0;
Linguagem de código: C# ( cs )
Quarto, chame o Filter()
método e passe uma instância do Callback
delegado que faz referência ao IsEven()
método. O Filter()
método retorna uma lista de números inteiros pares:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = Filter(numbers, IsEven);
Console.WriteLine("Even numbers:");
foreach (var number in evenNumbers)
{
Console.WriteLine($"{number}");
}
Linguagem de código: C# ( cs )
Saída:
Even numbers:
2
4
Linguagem de código: C# ( cs )
Quinto, chame o Filter()
método e passe uma instância do Callback
delegado que faz referência ao isOdd()
método. O Filter()
método retorna uma lista de números inteiros ímpares:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var oddNumbers = Filter(numbers, IsOdd);
Console.WriteLine("Odd numbers:");
foreach (var number in oddNumbers)
{
Console.WriteLine($"{number}");
}
Linguagem de código: C# ( cs )
Saída:
Odd numbers:
1
3
5
Linguagem de código: C# ( cs )
Junte tudo:
class Program
{
delegate bool Callback(int x);
static List<int> Filter(List<int> numbers, Callback callback)
{
var results = new List<int>();
foreach (var number in numbers)
{
if (callback(number))
{
results.Add(number);
}
}
return results;
}
static bool IsOdd(int x) => x % 2 != 0;
static bool IsEven(int x) => x % 2 == 0;
static void Main(string[] args)
{
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = Filter(numbers, IsEven);
Console.WriteLine("Even numbers:");
foreach (var number in evenNumbers)
{
Console.WriteLine($"{number}");
}
var oddNumbers = Filter(numbers, IsOdd);
Console.WriteLine("Odd numbers:");
foreach (var number in oddNumbers)
{
Console.WriteLine($"{number}");
}
}
}
Linguagem de código: C# ( cs )
Usando um delegado como retorno de chamada, você pode passar um método como argumento para outro método. Neste exemplo, o Filter()
método é muito dinâmico e pode aceitar qualquer método para filtrar a lista de inteiros.
Adicionando métodos a um delegado
Um delegado pode conter referências a vários métodos. Neste caso, o delegado é chamado de delegado multicast .
Para adicionar um método a um delegado, você usa o +=
operador. Por exemplo:
delegate void Greeting(string message);
class Program
{
static void SayHi(string name) => Console.WriteLine($"Hi {name}");
static void SayBye(string name) => Console.WriteLine($"Bye {name}");
static void Main(string[] args)
{
Greeting greeting = SayHi;
greeting += SayBye;
greeting("John");
}
}
Linguagem de código: JavaScript ( javascript )
Saída:
Hi John
Bye John
Como funciona.
Primeiro, defina o Greeting
tipo de delegado:
delegate void Greeting(string message);
Linguagem de código: JavaScript ( javascript )
A seguir, defina dois métodos estáticos SayHi()
e SayBye()
na Program
classe:
static void SayHi(string name) => Console.WriteLine($"Hi {name}");
static void SayBye(string name) => Console.WriteLine($"Bye {name}");
Linguagem de código: JavaScript ( javascript )
Em seguida, crie uma nova instância do Greeting
tipo delegado e atribua o SayHi
método à variável de saudação:
Greeting greeting = SayHi;
Depois disso, adicione um novo método à lista de invocação do greeting
delegado:
greeting += SayBye;
Finalmente, chame os métodos na lista de invocação do delegado de saudação:
greeting("John");
Linguagem de código: JavaScript ( javascript )
Esta instrução invoca o método SayHi()
and SayBye()
na lista de invocação do método de saudação. É importante observar que o delegado pode chamar os métodos da sua lista de invocação em qualquer ordem. Portanto, você não deve confiar na ordem dos métodos.
Os delegados são imutáveis. Isso significa que um delegado não pode ser alterado depois de criado. Portanto, o seguinte cria um novo delegado e o atribui à variável de saudação:
greeting += SayBye;
Removendo um método de um delegado
Para remover um método de um delegado, você usa o -=
operador. Observe que o C# emitirá um erro se você tentar chamar um delegado com uma lista de invocação vazia.
O exemplo a seguir ilustra como remover um método da lista de invocação de um delegado:
delegate void Greeting(string message);
class Program
{
static void SayHi(string name) => Console.WriteLine($"Hi {name}");
static void SayBye(string name) => Console.WriteLine($"Bye {name}");
static void Say(string message) => Console.WriteLine(message);
static void Main(string[] args)
{
Greeting greeting = SayHi;
greeting += Say;
greeting += SayBye;
greeting -= SayHi;
greeting("John");
}
}
Linguagem de código: C# ( cs )
Saída:
John
Bye John
Neste exemplo, antes de chamar os métodos, removemos o SayHi
método da lista de invocação do delegado de saudação:
greeting -= SayHi;
Linguagem de código: C# ( cs )
Se você remover todos os métodos da lista de invocação de um delegado, o delegado será nulo. Para invocar o delegado com uma verificação nula, você pode usar um operador condicional nulo como este:
greeting?.Invoke("John");
Linguagem de código: C# ( cs )
Resumo
- Um delegado é um tipo que faz referência a métodos com uma lista de parâmetros e tipo de retorno específicos.
- Use um delegado como retorno de chamada para passar um método como argumento para outro método.
- Os delegados são imutáveis.
- Use
+=
o operador para adicionar um método à lista de invocação de um delegado. - Use
-=
o operador para remover um método da lista de invocação de um delegado.