Qu'est-ce qu'une fermeture ? Les avons-nous en .NET?
S'ils existent dans .NET, pourriez-vous s'il vous plaît fournir un extrait de code (de préférence en C #) l'expliquant?
J'ai un article sur ce même sujet . (Il contient de nombreux exemples.)
En substance, une fermeture est un bloc de code qui peut être exécuté ultérieurement, mais qui maintient l'environnement dans lequel il a été créé pour la première fois - c'est-à-dire qu'il peut toujours utiliser les variables locales, etc. de la méthode qui l'a créé, même après cela. La méthode a terminé son exécution.
La fonctionnalité générale des fermetures est implémentée en C # par des méthodes anonymes et des expressions lambda.
Voici un exemple utilisant une méthode anonyme:
using System;
class Test
{
static void Main()
{
Action action = CreateAction();
action();
action();
}
static Action CreateAction()
{
int counter = 0;
return delegate
{
// Yes, it could be done in one statement;
// but it is clearer like this.
counter++;
Console.WriteLine("counter={0}", counter);
};
}
}
Production:
counter=1
counter=2
Ici, nous pouvons voir que l'action retournée par CreateAction a toujours accès à la variable compteur, et peut en effet l'incrémenter, même si CreateAction elle-même est terminée.
counter
est disponible pour être incrémenté - le compilateur génère une classe qui contient uncounter
champ, et tout code faisant référencecounter
finit par passer par une instance de cette classe.Si vous souhaitez voir comment C # implémente Closure, lisez "Je connais la réponse (son 42) blog"
Le compilateur génère une classe en arrière-plan pour encapsuler la méthode anoyme et la variable j
pour la fonction:
Le transformer en:
la source
Les fermetures sont des valeurs fonctionnelles qui conservent des valeurs variables de leur portée d'origine. C # peut les utiliser sous forme de délégués anonymes.
Pour un exemple très simple, prenez ce code C #:
À la fin de celui-ci, la barre sera définie sur 4 et le délégué myClosure peut être transmis pour être utilisé ailleurs dans le programme.
Les fermetures peuvent être utilisées pour de nombreuses choses utiles, comme l'exécution retardée ou pour simplifier les interfaces - LINQ est principalement construit à l'aide de fermetures. Pour la plupart des développeurs, la manière la plus immédiate qui soit utile consiste à ajouter des gestionnaires d'événements aux contrôles créés dynamiquement - vous pouvez utiliser des fermetures pour ajouter un comportement lorsque le contrôle est instancié, plutôt que de stocker des données ailleurs.
la source
Une fermeture est une fonction anonyme passée en dehors de la fonction dans laquelle elle est créée. Il conserve toutes les variables de la fonction dans laquelle il est créé qu'il utilise.
la source
Voici un exemple artificiel pour C # que j'ai créé à partir d'un code similaire en JavaScript:
Voici donc du code qui montre comment utiliser le code ci-dessus ...
J'espère que cela est quelque peu utile.
la source
Fondamentalement, la fermeture est un bloc de code que vous pouvez passer comme argument à une fonction. C # prend en charge les fermetures sous forme de délégués anonymes.
Voici un exemple simple:
La méthode List.Find peut accepter et exécuter un morceau de code (fermeture) pour rechercher l'élément de la liste.
En utilisant la syntaxe C # 3.0, nous pouvons écrire ceci comme:
la source
Une fermeture se produit lorsqu'une fonction est définie à l'intérieur d'une autre fonction (ou méthode) et qu'elle utilise les variables de la méthode parente . Cette utilisation de variables qui sont situées dans une méthode et enveloppées dans une fonction définie en son sein, est appelée une fermeture.
Mark Seemann a quelques exemples intéressants de fermetures dans son article de blog où il fait un parallèle entre la programmation oop et la programmation fonctionnelle.
Et pour le rendre plus détaillé
la source
Les fermetures sont des morceaux de code qui font référence à une variable en dehors d'eux-mêmes, (en dessous d'eux sur la pile), qui pourraient être appelés ou exécutés plus tard, (comme lorsqu'un événement ou un délégué est défini, et pourraient être appelés à un moment futur indéfini ) Parce que la variable externe que le bloc de références de code peut être hors de portée (et aurait sinon été perdue), le fait qu'elle soit référencée par le bloc de code (appelé une fermeture) indique au runtime de "maintenir "cette variable dans la portée jusqu'à ce qu'elle ne soit plus nécessaire par le morceau de fermeture du code ...
la source
J'ai essayé de le comprendre aussi, bien en dessous se trouvent les extraits de code pour le même code en Javascript et C # montrant la fermeture.
JavaScript:
C #:
JavaScript:
C #:
la source
Juste à l'improviste, une réponse simple et plus compréhensive du livre C # 7.0 en bref.
Pré-requis que vous devez savoir : Une expression lambda peut référencer les variables locales et les paramètres de la méthode dans laquelle elle est définie (variables externes).
Partie réelle : les variables externes référencées par une expression lambda sont appelées variables capturées. Une expression lambda qui capture des variables est appelée fermeture.
Dernier point à noter : les variables capturées sont évaluées lorsque le délégué est effectivement appelé, et non lorsque les variables ont été capturées:
la source
Si vous écrivez une méthode anonyme en ligne (C # 2) ou (de préférence) une expression Lambda (C # 3 +), une méthode réelle est toujours en cours de création. Si ce code utilise une variable locale de portée externe - vous devez toujours transmettre cette variable à la méthode d'une manière ou d'une autre.
par exemple, prenez cette clause Linq Where (qui est une méthode d'extension simple qui transmet une expression lambda):
si vous voulez utiliser i dans cette expression lambda, vous devez le passer à cette méthode créée.
La première question qui se pose est donc: doit-elle être transmise par valeur ou référence?
Le passage par référence est (je suppose) plus préférable car vous obtenez un accès en lecture / écriture à cette variable (et c'est ce que fait C #; je suppose que l'équipe de Microsoft a pesé le pour et le contre et a opté pour une référence par référence; selon Jon Skeet's article , Java est allé avec par valeur).
Mais alors une autre question se pose: où allouer ce i?
Doit-il réellement / naturellement être alloué sur la pile? Eh bien, si vous l'allouez sur la pile et la transmettez par référence, il peut y avoir des situations où elle survit à son propre cadre de pile. Prenez cet exemple:
L'expression lambda (dans la clause Where) crée à nouveau une méthode qui fait référence à un i. Si i est alloué sur la pile d'Outlive, au moment où vous énumérez les whereItems, le i utilisé dans la méthode générée pointera vers le i d'Outlive, c'est-à-dire vers un endroit de la pile qui n'est plus accessible.
Ok, nous en avons donc besoin sur le tas alors.
Donc, ce que le compilateur C # fait pour prendre en charge cette ligne anonyme / lambda, c'est utiliser ce qu'on appelle des " fermetures ": il crée une classe sur le tas appelée ( plutôt mal ) DisplayClass qui a un champ contenant le i, et la fonction qui utilise réellement il.
Quelque chose qui serait équivalent à cela (vous pouvez voir l'IL généré en utilisant ILSpy ou ILDASM):
Il instancie cette classe dans votre portée locale et remplace tout code relatif à i ou à l'expression lambda par cette instance de fermeture. Donc - chaque fois que vous utilisez le i dans votre code "portée locale" où j'ai été défini, vous utilisez en fait ce champ d'instance DisplayClass.
Donc, si je modifiais le "local" i dans la méthode principale, cela changerait en fait _DisplayClass.i;
c'est à dire
il affichera 12, car "i = 10" va dans ce champ dispalyclass et le modifie juste avant la 2ème énumération.
Une bonne source sur le sujet est ce module Bart De Smet Pluralsight (nécessite une inscription) (ignorez également son utilisation erronée du terme "Levage" - ce qu'il (je pense) veut dire, c'est que la variable locale (c'est-à-dire i) est modifiée pour se référer dans le nouveau champ DisplayClass).
Dans d'autres nouvelles, il semble y avoir une idée fausse selon laquelle les "fermetures" sont liées aux boucles - si je comprends bien, les "fermetures" ne sont PAS un concept lié aux boucles , mais plutôt aux méthodes anonymes / expressions lambda utilisant des variables de portée locales - bien que certaines astuces les questions utilisent des boucles pour le démontrer.
la source
Une fermeture est une fonction, définie au sein d'une fonction, qui peut accéder à ses variables locales ainsi qu'à son parent.
donc la fonction à l'intérieur de la méthode find.
peut accéder aux variables dans sa portée, t, et le nom de la variable qui est dans sa portée parent. Même s'il est exécuté par la méthode find en tant que délégué, à partir d'une autre étendue tous ensemble.
la source