web-dev-qa-db-fra.com

Pourquoi C # n'autorise pas les fonctions non membres comme C++

C # ne permettra pas d'écrire des fonctions non membres et chaque méthode devrait faire partie d'une classe. Je pensais cela comme une restriction dans toutes les langues de la CLI. Mais je me suis trompé et j'ai constaté que C++/CLI prend en charge des fonctions non membres. Une fois compilé, le compilateur fera de la méthode le membre d'une classe non nommée.

Voici ce que dit le standard C++/CLI,

[Remarque: les fonctions non membres sont traitées par la CLI en tant que membres d'une classe non nommée. toutefois, dans le code source C++/CLI, ces fonctions ne peuvent pas être qualifiées explicitement avec ce nom de classe. note de fin]

Le codage des fonctions non membres dans les métadonnées n'est pas spécifié. [Remarque: Cela ne pose pas de problème d'interopérabilité, car ces fonctions ne peuvent pas avoir une visibilité publique. note de fin]

Ma question est donc la suivante: pourquoi C # n’implémente-t-il pas quelque chose comme cela? Ou pensez-vous qu'il ne devrait pas y avoir de fonctions non membres et que chaque méthode devrait appartenir à une classe? 

Mon opinion est d'avoir un support de fonction non-membre et cela aide à éviter une interface de classe polluante. 

Des pensées..?

63
Navaneeth K N

Voir ce billet de blog:

http://blogs.msdn.com/ericlippert/archive/2009/06/22/why-doesn-t-c-implement-top-level-methods.aspx

(...)

On me demande "pourquoi C # n'implémente-t-il pas la fonctionnalité X?" tout le temps. La réponse est toujours la même: personne n’a jamais conçu, spécifié, mis en œuvre, testé, documenté et livré cette fonctionnalité. Ces six éléments sont nécessaires à la création d’une fonctionnalité. Tous coûtent énormément de temps, d’efforts et d’argent. Les fonctionnalités ne sont pas bon marché et nous nous efforçons de ne proposer que celles qui offrent les meilleurs avantages possibles aux utilisateurs, compte tenu de nos contraintes de temps, d'effort et d'argent.

Je comprends qu’une réponse aussi générale ne répond probablement pas à la question précise.

Dans ce cas particulier, l’avantage évident pour l’utilisateur était par le passé insuffisant pour justifier les complications de la langue qui en découleraient. En restreignant la manière dont différentes entités linguistiques s'imbriquent les unes dans les autres, nous (1) limitons les programmes juridiques à un style commun facile à comprendre et (2) permettons de définir des règles de "recherche d'identifiant" compréhensibles, spécifiables, pouvant être mises en oeuvre et testées et documentable.

En limitant le fait que les corps de méthodes soient toujours à l'intérieur d'une structure ou d'une classe, nous facilitons la discussion sur la signification d'un identificateur non qualifié utilisé dans un contexte d'invocation; une telle chose est toujours un membre invocable du type actuel (ou un type de base). 

(...)

et cet affichage de suivi:

http://blogs.msdn.com/ericlippert/archive/2009/06/24/it-already-is-a-scripting-language.aspx

(...)

Comme toutes les décisions de conception, lorsque nous sommes confrontés à plusieurs idées concurrentes, convaincantes, précieuses et impossibles à résoudre, nous devons trouver un compromis réalisable. Nous ne le faisons que par compte tenu de toutes les possibilités}, ce que nous faisons dans ce cas.

(souligné par le texte d'origine)

86
Eric Lippert

C # ne le permet pas car Java ne le permet pas. 

Je peux penser à plusieurs raisons pour lesquelles les concepteurs de Java ne l’ont probablement pas autorisé

  • Java a été conçu pour être simple. Ils ont essayé de créer une langue sans raccourcis aléatoires, de sorte que vous ne disposiez généralement que d'un moyen simple de tout faire, même si d'autres approches auraient été plus claires ou plus concises. Ils voulaient minimiser la courbe d'apprentissage, et apprendre "une classe peut contenir des méthodes" est plus simple que "une classe peut contenir des méthodes et des fonctions peuvent exister en dehors des classes".
  • À première vue, il semble moins orienté objet. (Tout ce qui ne fait pas partie d'un objet ne peut évidemment pas être orienté objet? Peut-être? Bien sûr, C++ dit oui, mais C++ n'a pas été impliqué dans cette décision)

Comme je l'ai déjà dit dans des commentaires, je pense que c'est une bonne question et qu'il existe de nombreux cas où des fonctions non-membres auraient été préférables. (cette partie est principalement une réponse à toutes les autres réponses en disant "vous n'en avez pas besoin")

En C++, où les fonctions non membres sont autorisées, elles sont souvent préférées pour plusieurs raisons:

  • Il facilite l'encapsulation. Moins les méthodes ont accès aux membres privés d'une classe, plus cette classe sera facile à refactoriser ou à maintenir. L'encapsulation est une partie importante de la programmation orientée objet.
  • Le code peut être réutilisé beaucoup plus facilement s'il ne fait pas partie d'une classe. Par exemple, la bibliothèque standard C++ définit std::find ou std :: sort` en tant que fonctions non membres, de sorte qu’elles puissent être réutilisées sur tout type de séquence, qu’il s’agisse de tableaux, de jeux, de listes chaînées ou (pour std :: find, au moins) les ruisseaux. La réutilisation du code est également une partie importante de la programmation orientée objet.
  • Cela nous donne un meilleur découplage. La fonction find n'a pas besoin de connaître la classe LinkedList pour pouvoir y travailler. Si elle avait été définie en tant que fonction membre, il s'agirait d'un membre de la classe LinkedList, fusionnant les deux concepts en un seul gros blob.
  • Extensibilité. Si vous acceptez que l'interface d'une classe ne soit pas simplement "tous ses membres publics", mais également "toutes les fonctions non membres agissant sur la classe", il devient alors possible d'étendre l'interface d'une classe sans avoir à éditer ou même recompiler la classe elle-même. 

La possibilité d'avoir des fonctions non membres peut avoir son origine avec C (où vous n'aviez pas d'autre choix), mais en C++ moderne, c'est une fonctionnalité vitale en soi, non seulement à des fins de comparabilité en amont, mais à cause de la simplicité. , code plus propre et plus réutilisable qu’il permet.

En fait, C # semble avoir réalisé à peu près les mêmes choses, beaucoup plus tard. Pourquoi pensez-vous que des méthodes d'extension ont été ajoutées? Ils constituent une tentative pour atteindre ce qui précède, tout en préservant la syntaxe simple semblable à Java ..... Lambda est également un exemple intéressant, car ce sont également de petites fonctions définies librement, non en tant que membres d’une classe particulière. Alors oui, le concept de fonctions non membres est utile, et les concepteurs de C # ont réalisé la même chose. Ils ont juste essayé d'introduire le concept par la porte arrière.

http://www.ddj.com/cpp/184401197 et http://www.gotw.ca/publications/mill02.htm sont deux articles écrits par des experts en C++ sur le sujet.

38
jalf

Les fonctions non membres sont une bonne chose car elles améliorent l’encapsulation et réduisent le couplage entre les types. La plupart des langages de programmation modernes tels que Haskell et F # prennent en charge des fonctions libres.

11
Nemanja Trifunovic

Quel est l'avantage de non placer chaque méthode dans une classe nommée? Pourquoi une fonction non membre "polluerait-elle" l'interface de la classe? Si vous ne le souhaitez pas dans l'API publique d'une classe, ne le rendez pas public ou ne le placez pas dans cette classe. Vous pouvez toujours créer une classe différente.

Je ne me souviens pas avoir jamais voulu écrire une méthode flottante sans la portée appropriée - autre que des fonctions anonymes, bien sûr (qui ne sont pas vraiment les mêmes).

En résumé, je ne vois aucun avantage à utiliser des fonctions non membres, mais je peux également constater des avantages en termes de cohérence, de nommage et de documentation en plaçant toutes les méthodes dans une classe nommée de manière appropriée.

9
Jon Skeet
  • Avoir tout le code dans les classes permet un ensemble plus puissant de capacités de réflexion .
  • Il permet l'utilisation d'initialisateurs statiques, qui peuvent initialiser les données nécessaires aux méthodes statiques d'une classe.
  • Cela évite les conflits de noms entre les méthodes en les incluant explicitement dans une unité à laquelle aucune autre unité de compilation ne peut ajouter.
3
Yuliy

La spécification de langage commun (CLS) indique que vous ne devriez pas avoir de fonctions non membres dans une bibliothèque conforme à la CLS. C'est comme un ensemble supplémentaire de restrictions en plus des restrictions de base de la CLI (interface de langage commun).

Il est possible qu'une future version de C # ajoute la possibilité d'écrire une directive using permettant d'accéder aux membres statiques d'une classe sans la qualification de nom de classe:

using System.Linq.Enumerable; // Enumerable is a static class

...

IEnumerable<int> range = Range(1, 10); // finds Enumerable.Range

Il ne sera alors pas nécessaire de changer le CLS et les bibliothèques existantes.

Ces billets de blog illustrent une bibliothèque pour la programmation fonctionnelle en C # et utilisent un nom de classe d'une lettre pour essayer de réduire le bruit généré par l'obligation de qualifier les appels de méthodes statiques. Des exemples comme celui-ci seraient un peu plus agréables si les directives using pouvaient cibler des classes.

3
Daniel Earwicker

Depuis Java, la plupart des programmeurs ont facilement accepté que toute méthode soit membre d'une classe. Je ne crée pas d'obstacles considérables et ne restreins pas le concept de méthode, ce qui facilite le langage.

Cependant, en effet, la classe infère l'objet et l'objet infère l'état, de sorte que le concept de classe contenant uniquement des méthodes statiques semble un peu absurde.

2
Dmitry Tashkinov

Rappelez-vous que le C++ est un langage beaucoup plus complexe que le C #. Et bien qu'ils puissent être similaires syntaxiquement, ce sont des bêtes très différentes sémantiquement. Vous ne penseriez pas qu'il serait terriblement difficile de faire un changement comme celui-ci, mais je pouvais voir comment cela pourrait être. ANTLR a une bonne page wiki appelée Qu'est-ce qui rend un problème de langue difficile? c'est bon à consulter pour des questions comme celle-ci. Dans ce cas:

Lexeur sensible au contexte? Vous ne pouvez pas choisir le symbole de vocabulaire auquel correspondre, à moins de savoir quel type de phrase vous analysez.

Maintenant, au lieu de simplement nous préoccuper des fonctions définies dans les classes, nous devons nous préoccuper des fonctions définies en dehors des classes. Conceptuellement, il n'y a pas beaucoup de différence. Mais en termes de lexing et d’analyse du code, vous avez maintenant le problème suivant: "si une fonction est en dehors d’une classe, elle appartient à cette classe non nommée. classe."

De plus, si le compilateur rencontre une méthode comme celle-ci:

public void Foo()
{
    Bar();
}

... il doit maintenant répondre à la question "Bar est-il situé dans cette classe ou est-ce une classe globale?"

Références en avant ou externes? C'est-à-dire plusieurs passes nécessaires? Pascal a une référence "forward" pour gérer les références de procédure intra-fichier, mais les références à des procédures dans d'autres fichiers via les clauses USES, etc. nécessitent un traitement spécial.

Ceci est une autre chose qui pose des problèmes. Rappelez-vous que C # ne nécessite pas de déclaration en aval. Le compilateur fera un passage pour déterminer quelles classes sont nommées et quelles fonctions ces classes contiennent. Maintenant, vous devez vous préoccuper de trouver des fonctions de classes et, fonctions pouvant être à l'intérieur ou à l'extérieur d'une classe. C'est quelque chose qu'un analyseur C++ n'a pas à s'inquiéter, car il analyse tout dans l'ordre.

Maintenant, ne vous méprenez pas, cela pourrait probablement être fait en C #, et j'utiliserais probablement une telle fonctionnalité. Mais vaut-il vraiment la peine de surmonter ces obstacles lorsque vous pouvez simplement taper un nom de classe devant une méthode statique?

1
Jason Baker

Je pense que vous avez vraiment besoin de clarifier ce que vous voudriez créer par des méthodes statiques non-membres.

Par exemple, certaines des choses pour lesquelles vous pourriez les souhaiter pourraient être traitées avec Méthodes d'extension

Une autre utilisation typique (d'une classe qui ne contient que des méthodes statiques) est dans une bibliothèque. Dans ce cas, il y a peu de mal à créer une classe dans un Assembly entièrement composé de méthodes statiques. Cela les maintient ensemble, évite les collisions de nommage. Après tout, il existe des méthodes statiques en mathématiques qui ont le même objectif.

En outre, vous ne devez pas nécessairement comparer le modèle objet de C++ avec C #. Le C++ est en grande partie (mais pas parfaitement) compatible avec le C, qui n’avait pas du tout de système de classes. C++ a donc dû prendre en charge cet idiome de programmation hors de l’héritage C, pas pour un impératif de conception particulier.

1
Cade Roux

Les fonctions gratuites sont très utiles si vous les combinez avec la frappe à l'aide de canards. L'ensemble de la STL C++ est basé sur celle-ci. Par conséquent, je suis sûr que C # introduira des fonctions libres quand ils parviendront à ajouter de vrais génériques. 

A l'instar de l'économie, la conception du langage concerne également la psychologie. Si vous créez un appétit pour les vrais génériques via des fonctions gratuites en C # et ne livrez pas, vous tuez C #. Tous les développeurs C # passeraient alors au C++ et personne ne souhaite que cela se produise, pas la communauté C # et très certainement pas ceux investis dans C++.

0
Patrick Fromberg

S'il est vrai que vous avez besoin d'une classe (par exemple, une classe statique appelée FreeFunctions) pour contenir de telles fonctions, vous êtes libre de placer using static FreeFunctions; en haut de tout fichier nécessitant ces fonctions, sans avoir à ajouter du code FreeFunctions. à votre code. Je ne suis pas sûr s'il existe réellement un cas où cela est manifestement inférieur à ne pas exiger que les définitions de fonctions soient contenues dans une classe.

0
Dylan Nicholson