Article précédent L'histoire derrière – Happy Lambda Expression (1) Nous avons analysé l'expression Lambda du moins profond au plus profond. Nous connaissons la différence entre celui-ci et les délégués et les méthodes ordinaires, et comparons leurs performances grâce à des tests. Ensuite, nous avons une compréhension approfondie des expressions Lambda via le code IL et expliquons comment utiliser les expressions Lambda dans .NET pour implémenter certains modèles JavaScript populaires. .
Aujourd'hui, examinons d'autres nouvelles façons d'utiliser les expressions Lambda dans .NET.
Les expressions Lambda jouent avec le polymorphisme
Comment Lambda implémente-t-il le polymorphisme ? Nous utilisons des classes abstraites et des méthodes virtuelles, pourquoi utilisons-nous encore Lambda ? Regardons le code suivant :
class MyBaseClass { public Action SomeAction { get; protected set; } public MyBaseClass() { SomeAction = () => { //Do something! }; } } class MyInheritedClass : MyBaseClass { public MyInheritedClass() { SomeAction = () => { //Do something different! }; } }
Notre classe de base n'est pas une classe abstraite et n'a pas de méthodes virtuelles, mais elle expose les propriétés par délégation, puis les réaffecte à notre SomeAction dans la sous-classe. .nouvelle expression. C'est notre processus de réalisation du polymorphisme. Bien entendu, l'ensemble de SomeAction dans la classe parent a un niveau de protection protégé, sinon il sera facilement modifié par l'extérieur. Mais ce n'est pas encore parfait. Une fois la SomeAction de la classe parent écrasée dans la sous-classe, nous ne pouvons plus y accéder du tout. La situation réelle est que nous pouvons accéder à la méthode originale de la classe parent via base. L'étape suivante consiste à implémenter ceci
class MyBaseClass { public Action SomeAction { get; private set; } Stack<Action> previousActions; protected void AddSomeAction(Action newMethod) { previousActions.Push(SomeAction); SomeAction = newMethod; } protected void RemoveSomeAction() { if(previousActions.Count == 0) return; SomeAction = previousActions.Pop(); } public MyBaseClass() { previousActions = new Stack<Action>(); SomeAction = () => { //Do something! }; } }
Dans le code ci-dessus, nous utilisons AddSomeAction pour réaliser l'écrasement et enregistrer la méthode d'origine dans previousActions. De cette façon, nous pouvons garder les deux existants en même temps.
Tout le monde sait que les sous-classes ne peuvent pas remplacer les méthodes statiques des classes parentes, mais que se passe-t-il si nous voulons implémenter une couverture de méthodes statiques ?
void Main() { var mother = HotDaughter.Activator().Message; //mother = "I am the mother" var create = new HotDaughter(); var daughter = HotDaughter.Activator().Message; //daughter = "I am the daughter" } class CoolMother { public static Func<CoolMother> Activator { get; protected set; } //We are only doing this to avoid NULL references! static CoolMother() { Activator = () => new CoolMother(); } public CoolMother() { //Message of every mother Message = "I am the mother"; } public string Message { get; protected set; } } class HotDaughter : CoolMother { public HotDaughter() { //Once this constructor has been "touched" we set the Activator ... Activator = () => new HotDaughter(); //Message of every daughter Message = "I am the daughter"; } }
Ici, nous profitons toujours du fait que les expressions Lambda sont utilisées comme attributs et peuvent être réaffectées à tout moment. Bien sûr, ce n'est qu'un exemple simple, et il n'est pas recommandé à tout le monde de le faire dans des projets réels.
Dictionnaire des méthodes
En fait, nous avons déjà mentionné ce mode dans la méthode de retour de l'article précédent, mais un tel nom n'existe pas, ce n'est donc qu'un résumé. L'histoire est la suivante : avez-vous souvent l'impression que les déclarations de changement de casse ne sont pas assez élégantes lorsque vous les écrivez ? Mais vous ne voulez pas passer en mode usine ou en mode stratégie, alors comment rendre votre code plus avancé ?
public Action GetFinalizer(string input) { switch { case "random": return () => { /* ... */ }; case "dynamic": return () => { /* ... */ }; default: return () => { /* ... */ }; } } //-------------------变身之后----------------------- Dictionary<string, Action> finalizers; public void BuildFinalizers() { finalizers = new Dictionary<string, Action>(); finalizers.Add("random", () => { /* ... */ }); finalizers.Add("dynamic", () => { /* ... */ }); } public Action GetFinalizer(string input) { if(finalizers.ContainsKey(input)) return finalizers[input]; return () => { /* ... */ }; }
Il semble avoir un aspect différent, avec un peu de goût. Mais quand j'y pense, toutes les méthodes doivent être mises dans les BuildFinalizers. Cette méthode d'organisation est vraiment inacceptable. Apprenons la méthode de développement de plug-ins et laissons-la trouver par elle-même toutes les méthodes dont nous avons besoin.
static Dictionary<string, Action> finalizers; // 在静态的构造函数用调用这个方法 public static void BuildFinalizers() { finalizers = new Dictionary<string, Action>(); // 获得当前运行程序集下所有的类型 var types = Assembly.GetExecutingAssembly().GetTypes(); foreach(var type in types) { // 检查类型,我们可以提前定义接口或抽象类 if(type.IsSubclassOf(typeof(MyMotherClass))) { // 获得默认无参构造函数 var m = type.GetConstructor(Type.EmptyTypes); // 调用这个默认的无参构造函数 if(m != null) { var instance = m.Invoke(null) as MyMotherClass; var name = type.Name.Remove("Mother"); var method = instance.MyMethod; finalizers.Add(name, method); } } } } public Action GetFinalizer(string input) { if(finalizers.ContainsKey(input)) return finalizers[input]; return () => { /* ... */ }; }
Si nous voulons implémenter un plug-in, nous devons non seulement pouvoir charger les méthodes dans cet assembly, mais également pouvoir charger des méthodes externes à tout moment ou même pendant l'exécution. Veuillez continuer. lecture :
internal static void BuildInitialFinalizers() { finalizers = new Dictionary<string, Action>(); LoadPlugin(Assembly.GetExecutingAssembly()); } public static void LoadPlugin(Assembly assembly) { var types = assembly.GetTypes(); foreach(var type in types) { if(type.IsSubclassOf(typeof(MyMotherClass))) { var m = type.GetConstructor(Type.EmptyTypes); if(m != null) { var instance = m.Invoke(null) as MyMotherClass; var name = type.Name.Remove("Mother"); var method = instance.MyMethod; finalizers.Add(name, method); } } } }
Maintenant, nous pouvons utiliser cette méthode pour spécifier l'assembly pour charger ce dont nous avons besoin.
Enfin, je vous laisse avec une question, peut-on écrire des expressions récursives ? Comment écrire la méthode suivante à l’aide d’expressions ?
int factorial(int n) { if(n == 0) return 1; else return n * factorial(n - 1); }
Ce qui précède est le contenu de l'histoire derrière - Happy Lambda Expression (2). Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !