Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Wanneer u een methode toewijst aan een gemachtigde, bieden covariantie en contravariantie flexibiliteit voor het koppelen van een gedelegeerdetype aan een methodehandtekening. Met covariantie kan een methode een returntype hebben dat meer afgeleid is dan dat in de delegate is gedefinieerd. Contravariantie staat een methode toe waarvan de parametertypen minder afgeleid zijn dan die in het delegate type.
Voorbeeld 1: Covariantie
Beschrijving
In dit voorbeeld ziet u hoe gemachtigden kunnen worden gebruikt met methoden die retourtypen hebben die zijn afgeleid van het retourtype in de handtekening voor gemachtigden. Het gegevenstype dat wordt geretourneerd door DogsHandler
is van het type Dogs
, dat is afgeleid van het Mammals
type dat is gedefinieerd in de gemachtigde.
Code
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;
}
}
Voorbeeld 2: Contravariantie
Beschrijving
In dit voorbeeld ziet u hoe gemachtigden kunnen worden gebruikt met methoden met parameters waarvan de typen basistypen zijn van het parametertype voor de gedelegeerde handtekening. Met contravariantie kunt u één gebeurtenishandler gebruiken in plaats van afzonderlijke handlers. In het volgende voorbeeld worden twee gemachtigden gebruikt:
Een aangepaste
KeyEventHandler
gemachtigde die de handtekening van een sleutelgebeurtenis definieert. De handtekening is:public delegate void KeyEventHandler(object sender, KeyEventArgs e)
Een aangepaste
MouseEventHandler
delegate die de structuur van een muisgebeurtenis definieert. De handtekening is:public delegate void MouseEventHandler(object sender, MouseEventArgs e)
Het voorbeeld definieert een gebeurtenis-handler met een EventArgs parameter en gebruikt deze om zowel sleutel- als muis-gebeurtenissen te verwerken. Dit werkt omdat EventArgs een basistype is van zowel de custom KeyEventArgs
- als MouseEventArgs
-klassen zoals gedefinieerd in het voorbeeld. Met Contravariantie kan een methode die een parameter van het basistype accepteert, worden gebruikt voor gebeurtenissen die afgeleide typeparameters bieden.
Hoe contravariantie werkt in dit voorbeeld
Wanneer u zich abonneert op een gebeurtenis, controleert de compiler of uw gebeurtenis-handlermethode compatibel is met de handtekening voor gemachtigden van de gebeurtenis. Met contravariantie:
- De
KeyDown
gebeurtenis verwacht een methode dieKeyEventArgs
als parameter neemt. - De
MouseClick
gebeurtenis verwacht een methode dieMouseEventArgs
accepteert. - Uw
MultiHandler
methode gebruikt het basistypeEventArgs
. - Aangezien
KeyEventArgs
enMouseEventArgs
beide overnemen vanEventArgs
, kunnen ze veilig worden doorgegeven aan een methode dieEventArgs
verwacht. - De compiler staat deze toewijzing toe omdat deze veilig is - de
MultiHandler
kan met elkeEventArgs
instantie werken.
Dit is contravariantie in actie: u kunt een methode gebruiken met een parameter 'minder specifiek' (basistype), waarbij een parameter 'specifieker' (afgeleid type) wordt verwacht.
Code
// 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);
}
}
Belangrijkste punten over contravariantie
// 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
Wanneer u dit voorbeeld uitvoert, ziet u dat met dezelfde MultiHandler
methode zowel sleutel- als muisevenementen worden verwerkt, waarmee wordt gedemonstreerd hoe contravariantie flexibelere en herbruikbare code voor het verwerken van gebeurtenissen mogelijk maakt.