A delegate is a type that represents a reference to a method with a specific signature. Once you have a delegate type, any method that matches that signature, a named method, a static method, even a lambda, can be assigned to it and passed around like any other value. This article builds a small Notify delegate and uses it to switch a notification channel from SMS to email without changing the class that sends it.
Ένα delegate είναι ένας τύπος που αντιπροσωπεύει μια αναφορά σε μια μέθοδο με συγκεκριμένη υπογραφή. Μόλις έχεις έναν τύπο delegate, οποιαδήποτε μέθοδος ταιριάζει με αυτή την υπογραφή, μια named μέθοδος, μια static μέθοδος, ακόμα και ένα lambda, μπορεί να ανατεθεί σε αυτόν και να περάσει σαν οποιαδήποτε άλλη τιμή. Αυτό το άρθρο χτίζει ένα μικρό delegate Notify και το χρησιμοποιεί για να αλλάξει ένα κανάλι ειδοποίησης από SMS σε email χωρίς να αλλάξει η κλάση που το στέλνει.
Define a delegate: Notify
Ορισμός ενός delegate: Notify
A delegate declaration describes a method shape: a return type and a parameter list, nothing else. Any method with that exact shape can be used wherever the delegate type is expected.
Μια δήλωση delegate περιγράφει το σχήμα μιας μεθόδου: έναν τύπο επιστροφής και μια λίστα παραμέτρων, τίποτα άλλο. Οποιαδήποτε μέθοδος έχει ακριβώς αυτό το σχήμα μπορεί να χρησιμοποιηθεί όπου αναμένεται ο τύπος delegate.
public delegate void Notify(string message);
Two methods match that shape, one for SMS and one for email:
Δύο μέθοδοι ταιριάζουν με αυτό το σχήμα, μία για SMS και μία για email:
public static class Notifications
{
public static void BySms(string message) => Console.WriteLine($"[SMS] {message}");
public static void ByEmail(string message) => Console.WriteLine($"[Email] {message}");
}
Pass Notify as a parameter
Πέρασμα του Notify ως παράμετρος
A class that needs to notify someone does not need to know whether that means SMS or email. It only needs a Notify parameter and calls it when the moment comes:
Μια κλάση που χρειάζεται να ειδοποιήσει κάποιον δεν χρειάζεται να ξέρει αν αυτό σημαίνει SMS ή email. Χρειάζεται μόνο μια παράμετρο Notify και την καλεί όταν έρθει η στιγμή:
public class OrderService
{
public void PlaceOrder(string orderId, Notify notify)
{
// order placing logic here
notify($"Order {orderId} has been placed.");
}
}
OrderService never mentions SMS, email or any other channel. It only knows about the Notify shape, so the caller decides which method actually runs.
Το OrderService δεν αναφέρει ποτέ SMS, email ή οποιοδήποτε άλλο κανάλι. Ξέρει μόνο για το σχήμα Notify, οπότε ο καλών αποφασίζει ποια μέθοδος τρέχει τελικά.
Swap SMS for email: OrderService never changes
Αλλαγή από SMS σε email: το OrderService δεν αλλάζει ποτέ
Passing a different method is the entire change needed to switch notification channels:
Το να περάσεις διαφορετική μέθοδο είναι όλη η αλλαγή που χρειάζεται για να αλλάξεις κανάλι ειδοποίησης:
var orders = new OrderService();
Console.WriteLine("Today: notify by SMS");
orders.PlaceOrder("A-1001", Notifications.BySms);
Console.WriteLine();
Console.WriteLine("Later: switch to email");
orders.PlaceOrder("A-1002", Notifications.ByEmail);
Same PlaceOrder method, same OrderService class, two different notification channels. Nothing inside OrderService changed, only which method the caller passed in for notify.
Ίδια μέθοδος PlaceOrder, ίδια κλάση OrderService, δύο διαφορετικά κανάλια ειδοποίησης. Τίποτα μέσα στο OrderService δεν άλλαξε, μόνο ποια μέθοδος πέρασε ο καλών για το notify.
Lambdas work too
Δουλεύουν και τα lambdas
Notify does not care whether the method behind it has a name. A lambda matches the same shape and can be passed directly:
Το Notify δεν νοιάζεται αν η μέθοδος από πίσω του έχει όνομα. Ένα lambda ταιριάζει με το ίδιο σχήμα και μπορεί να περαστεί απευθείας:
orders.PlaceOrder("A-1003", message => Console.WriteLine($"[Push] {message}"));
No named method, no separate class, just an inline block of code that matches the Notify shape. OrderService still has no idea what is behind the delegate it was given.
Καμία named μέθοδος, καμία ξεχωριστή κλάση, μόνο ένα inline κομμάτι κώδικα που ταιριάζει με το σχήμα του Notify. Το OrderService ακόμα δεν έχει ιδέα τι κρύβεται πίσω από το delegate που του δόθηκε.
All three channels together, runnable and editable:
Και τα τρία κανάλια μαζί, εκτελέσιμα και επεξεργάσιμα:
Takeaways
Συμπεράσματα
- A delegate describes a method shape, a return type and parameters, so any matching method or lambda can be assigned to it.
- A class that accepts a delegate parameter, like
OrderService.PlaceOrder(string, Notify), depends only on that shape, never on a specific implementation such as SMS or email. - Swapping behavior means passing a different method or lambda at the call site. The class holding the delegate parameter never changes.
- This is the same idea as virtual and override, expressed with a method reference instead of a class hierarchy: depend on a shape, not on one specific implementation.
- Ένα delegate περιγράφει το σχήμα μιας μεθόδου, έναν τύπο επιστροφής και παραμέτρους, οπότε οποιαδήποτε ταιριαστή μέθοδος ή lambda μπορεί να ανατεθεί σε αυτό.
- Μια κλάση που δέχεται μια παράμετρο delegate, όπως το
OrderService.PlaceOrder(string, Notify), εξαρτάται μόνο από αυτό το σχήμα, ποτέ από συγκεκριμένη υλοποίηση όπως SMS ή email. - Η αλλαγή συμπεριφοράς σημαίνει να περάσεις διαφορετική μέθοδο ή lambda στο σημείο κλήσης. Η κλάση που κρατά την παράμετρο delegate δεν αλλάζει ποτέ.
- Αυτή είναι η ίδια ιδέα με το virtual και override, εκφρασμένη με αναφορά μεθόδου αντί για ιεραρχία κλάσεων: εξάρτηση από ένα σχήμα, όχι από μία συγκεκριμένη υλοποίηση.