Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Quando você atribui um método a um delegado, a covariância e a contravariância fornecem flexibilidade para corresponder um tipo de delegado com uma assinatura de método. A covariância permite que um método tenha um tipo de retorno mais derivado do que o definido no delegado. A contravariância permite um método que tem tipos de parâmetros que são menos derivados do que aqueles no tipo delegado.
Exemplo 1: Covariância
Descrição
Este exemplo demonstra como os delegados podem ser usados com métodos que têm tipos de retorno derivados do tipo de retorno na assinatura do delegado. O tipo de dados retornado por DogsHandler é do tipo Dogs, que deriva do Mammals tipo definido no delegado.
Código
class Mammals {}
class Dogs : Mammals {}
class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();
public static Mammals MammalsHandler()
{
return null;
}
public static Dogs DogsHandler()
{
return null;
}
static void Test()
{
HandlerMethod handlerMammals = MammalsHandler;
// Covariance enables this assignment.
HandlerMethod handlerDogs = DogsHandler;
}
}
Exemplo 2: Contravariância
Descrição
Este exemplo demonstra como os delegados podem ser usados com métodos que têm parâmetros cujos tipos são tipos base do tipo de parâmetro de assinatura delegada. Com contravariância, você pode usar um manipulador de eventos em vez de manipuladores separados. O exemplo a seguir usa dois delegados:
Um delegado personalizado
KeyEventHandlerque define a assinatura de um evento chave. A sua assinatura é:public delegate void KeyEventHandler(object sender, KeyEventArgs e)Um delegado personalizado
MouseEventHandlerque define a assinatura de um evento do mouse. A sua assinatura é:public delegate void MouseEventHandler(object sender, MouseEventArgs e)
O exemplo define um manipulador de eventos com um EventArgs parâmetro e o usa para manipular eventos de tecla e mouse. Isso funciona porque EventArgs é um tipo base do personalizado KeyEventArgs e MouseEventArgs das classes definidas no exemplo. A contravariância permite que um método que aceita um parâmetro de tipo base seja usado para eventos que fornecem parâmetros de tipo derivados.
Como funciona a contravariância neste exemplo
Quando você se inscreve em um evento, o compilador verifica se seu método manipulador de eventos é compatível com a assinatura de representante do evento. Com contravariância:
- O
KeyDownevento espera um método que aceitaKeyEventArgs. - O
MouseClickevento espera um método que recebeMouseEventArgs. - O seu
MultiHandlermétodo aceita o tipo baseEventArgs. - Uma vez que
KeyEventArgseMouseEventArgsambos herdam deEventArgs, eles podem ser passados com segurança para um método esperandoEventArgs. - O compilador permite essa atribuição porque é seguro - o
MultiHandlerpode trabalhar com qualquerEventArgsinstância.
Esta é a contravariância em ação: você pode usar um método com um parâmetro "menos específico" (tipo base) onde um parâmetro "mais específico" (tipo derivado) é esperado.
Código
// Custom EventArgs classes to demonstrate the hierarchy
public class KeyEventArgs(string keyCode) : EventArgs
{
public string KeyCode { get; set; } = keyCode;
}
public class MouseEventArgs(int x, int y) : EventArgs
{
public int X { get; set; } = x;
public int Y { get; set; } = y;
}
// Define delegate types that match the Windows Forms pattern
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
public delegate void MouseEventHandler(object sender, MouseEventArgs e);
// A simple class that demonstrates contravariance with events
public class Button
{
// Events that expect specific EventArgs-derived types
public event KeyEventHandler? KeyDown;
public event MouseEventHandler? MouseClick;
// Method to simulate key press
public void SimulateKeyPress(string key)
{
Console.WriteLine($"Simulating key press: {key}");
KeyDown?.Invoke(this, new KeyEventArgs(key));
}
// Method to simulate mouse click
public void SimulateMouseClick(int x, int y)
{
Console.WriteLine($"Simulating mouse click at ({x}, {y})");
MouseClick?.Invoke(this, new MouseEventArgs(x, y));
}
}
public class Form1
{
private Button button1;
public Form1()
{
button1 = new Button();
// Event handler that accepts a parameter of the base EventArgs type.
// This method can handle events that expect more specific EventArgs-derived types
// due to contravariance in delegate parameters.
// You can use a method that has an EventArgs parameter,
// although the KeyDown event expects the KeyEventArgs parameter.
button1.KeyDown += MultiHandler;
// You can use the same method for an event that expects
// the MouseEventArgs parameter.
button1.MouseClick += MultiHandler;
}
// Event handler that accepts a parameter of the base EventArgs type.
// This works for both KeyDown and MouseClick events because:
// - KeyDown expects KeyEventHandler(object sender, KeyEventArgs e)
// - MouseClick expects MouseEventHandler(object sender, MouseEventArgs e)
// - Both KeyEventArgs and MouseEventArgs derive from EventArgs
// - Contravariance allows a method with a base type parameter (EventArgs)
// to be used where a derived type parameter is expected
private void MultiHandler(object sender, EventArgs e)
{
Console.WriteLine($"MultiHandler called at: {DateTime.Now:HH:mm:ss.fff}");
// You can check the actual type of the event args if needed
switch (e)
{
case KeyEventArgs keyArgs:
Console.WriteLine($" - Key event: {keyArgs.KeyCode}");
break;
case MouseEventArgs mouseArgs:
Console.WriteLine($" - Mouse event: ({mouseArgs.X}, {mouseArgs.Y})");
break;
default:
Console.WriteLine($" - Generic event: {e.GetType().Name}");
break;
}
}
public void DemonstrateEvents()
{
Console.WriteLine("Demonstrating contravariance in event handlers:");
Console.WriteLine("Same MultiHandler method handles both events!\n");
button1.SimulateKeyPress("Enter");
button1.SimulateMouseClick(100, 200);
button1.SimulateKeyPress("Escape");
button1.SimulateMouseClick(50, 75);
}
}
Pontos-chave sobre a contravariância
// Demonstration of how contravariance works with delegates:
//
// 1. KeyDown event signature: KeyEventHandler(object sender, KeyEventArgs e)
// where KeyEventArgs derives from EventArgs
//
// 2. MouseClick event signature: MouseEventHandler(object sender, MouseEventArgs e)
// where MouseEventArgs derives from EventArgs
//
// 3. Our MultiHandler method signature: MultiHandler(object sender, EventArgs e)
//
// 4. Contravariance allows us to use MultiHandler (which expects EventArgs)
// for events that provide more specific types (KeyEventArgs, MouseEventArgs)
// because the more specific types can be safely treated as their base type.
//
// This is safe because:
// - The MultiHandler only uses members available on the base EventArgs type
// - KeyEventArgs and MouseEventArgs can be implicitly converted to EventArgs
// - The compiler knows that any EventArgs-derived type can be passed safely
Ao executar este exemplo, você verá que o mesmo MultiHandler método manipula com êxito eventos de chave e mouse, demonstrando como a contravariância permite um código de manipulação de eventos mais flexível e reutilizável.