Est-ce que C # devient plus difficile à lire?

15

Au fur et à mesure que C # a progressé, de nombreuses fonctionnalités de langage ont été ajoutées. C'est arrivé au point où cela devient illisible pour moi.

À titre d'exemple, considérons l'extrait de code suivant du code Caliburn.Micro ici :

            container = CompositionHost.Initialize(
                   new AggregateCatalog(
                      AssemblySource.Instance.
                             Select(x => new AssemblyCatalog(x))
                               .OfType<ComposablePartCatalog>()
                )
            );

Maintenant, ce n'est qu'un petit exemple.

J'ai un certain nombre de questions:

  • Est-ce un problème courant ou connu?
  • La communauté C # trouve-t-elle la même chose?
  • Est-ce un problème avec la langue ou est-ce le style utilisé par le développeur?

Existe-t-il des solutions simples pour mieux comprendre le code des autres et éviter d'écrire du code de cette manière?

Steven Evers
la source
6
C'est la fonction lambda, ils sont difficiles à lire jusqu'à ce que vous les obteniez vraiment.
Ryathal
9
Je pense que vous ne connaissiez pas vraiment la syntaxe aussi bien que vous le pensiez. Avec la pratique de la lecture, votre exemple est simple.
ChaosPandion
6
@Thomas Owens: Holy shit man, donnez-nous au moins un peu de temps pour éditer les questions pour les garder ouvertes. 15 minutes? Allez donc.
Steven Evers
4
@DannyVarod: 1. "Non" n'est pas une réponse acceptable 2. La question est close 3. Si vous acceptez que la question ne devrait pas être ici, signalez / votez pour la fermer ou la modifier pour être meilleure .
Steven Evers
2
@ Thorbjørn Ravn Andersen - Que peut-on apprendre d'une gifle? Cela ne contient aucune information!

Réponses:

12

Une note rapide sur l'emplacement du langage devrait le clarifier: C # est un langage de programmation à usage général ; contrairement à C (comme C ++), il aspire à une abstraction élevée; contrairement aux dialectes lisp, il vise une expressivité pratique et, contrairement à Java, il est plus agressif - Microsoft répond rapidement à la demande.

C'est pourquoi cela se transforme en un mélange de LINQ, lambdas et de nouveaux mots-clés étranges - il s'adapte rapidement à de nouveaux domaines problématiques, et c'est en effet une pente glissante vers un langage si complexe que très peu peuvent l'utiliser correctement (comme C ++). Ce n'est pas un problème avec C # lui-même, c'est un problème avec n'importe quel langage avec ces objectifs ambitieux.

La communauté en est consciente et, plus important encore, les gars derrière C # en sont parfaitement conscients (les quelques entrées de blog et podcasts sur ce qui était derrière les nouveaux ajouts en C # 5.0 montrent à quel point ces gars veulent garder les choses simples). Microsoft est tente de prendre une partie de la charge de leur navire amiral de telle sorte qu'il ne devienne pas un tarpit: l'introduction du DLR, un spot lumineux sur de nouvelles langues (F #).

De plus, le matériel de cours sur C # (y compris les certifications MS) recommande des styles d'utilisation différents (mais cohérents) pour C # en fonction du problème - choisissez votre arme et respectez-la: LINQ de style fluide sur certains problèmes, lambdas avec TPL sur d'autres, simple -old pour la plupart.

vski
la source
2
Pouvez-vous expliquer ce que vous voulez dire par "contrairement aux dialectes lisp, il vise l'expressivité pratique"?
Giorgio
27

Non. C # nous donne plus d'options pour écrire du code plus succinctement. Cela peut être utilisé ou abusé. S'il est utilisé correctement, il facilite la lecture du code. S'il est mal utilisé, il peut rendre le code plus difficile à lire. Mais les mauvais programmeurs ont toujours eu la capacité d'écrire du code difficile à lire.

Prenons deux exemples, tous deux basés sur des exemples trouvés sur StackOverflow concernant le même problème, en trouvant des types avec un attribut spécifique:

C # 2.0:

static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{
    foreach(Assembly assembly in AppDomain.Current.GetAssemblies())
    {
        foreach(Type type in assembly.GetTypes()) 
        {
            if (type.IsDefined(typeof(TAttribute),inherit))
                yield return type;
        }
    }
}

C # 4.0:

IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{ 
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where type.IsDefined(typeof(TAttribute),inherit)
           select type;
}

À mon humble avis, la nouvelle syntaxe le rend beaucoup plus facile à lire.

Pete
la source
la nouvelle syntaxe est en effet plus facile à lire, mais plus facile à comprendre, comme dans ce qui se passe sous le capot? Le premier exemple est compréhensible, le second est plus lisible.
nawfal
4
@nawfal D'après mon expérience, les gens ont beaucoup plus de mal à comprendre les constructions "yield return" que les instructions linq; donc je dirais que le second est plus lisible et compréhensible. Ni l'un ni l'autre ne vous dit vraiment ce qui se passe sous le capot, car les deux correspondent à des abstractions loin de la façon dont les processeurs fonctionnent réellement.
Chuu
Pour être honnête, je n'aime pas LINQ car il résume les boucles et encourage les codeurs à récupérer des données dans des requêtes LINQ distinctes (avec des boucles distinctes en arrière-plan) au lieu d'une boucle avec un noyau plus compliqué.
mg30rg du
8

Lorsque LINQ a été ajouté, il a popularisé un style de codage impliquant de nombreux chaînages de méthodes de style Fluent et le passage de lambdas en tant que paramètres.

Ce style est très puissant une fois que vous êtes à l'aise avec lui. Cependant, il peut être utilisé à mauvais escient pour rendre le code assez illisible. Je ne pense pas que votre exemple soit trop mauvais, bien que l'indentation soit plutôt aléatoire (et c'est une caractéristique courante de ce style de code, Visual Studio ne l'indent pas automatiquement de manière très cohérente).

Sur mon lieu de travail, nous avons discuté de ce style de codage lors de la révision de nos normes de codage plus tôt cette année, et nous avons décidé d'encourager les développeurs à décomposer le code comme ça: en particulier, à ne pas créer et initialiser des objets anonymes dans un appel de fonction. Votre code deviendrait donc:

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);
Carson63000
la source
1
N'est-il pas étrange de créer un nouveau type, puis d'activer dynamiquement ce type juste après?
DeadMG
Probablement, je viens de couper l'extrait de code sans vraiment prendre en compte son intention. Je voulais juste diviser toutes les instanciations en leurs propres lignes de code pour montrer quel effet cela aurait sur l'apparence du code.
Carson63000
Je prends le blâme pour l'indentation :(). Voici l'original: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/…
1
Je suis entièrement d'accord avec cette réponse. Le problème dans l'exemple de code n'est pas la nouvelle syntaxe C #, c'est que l'auteur a essayé d'être intelligent avec un code "à une ligne". Il y a cette vieille règle qui remonte à UNIX: tout ne devrait faire qu'une chose, et bien le faire. Elle s'applique à la classe, elle s'applique à la méthode et, bien sûr, elle s'applique très certainement aux lignes de code.
Laurent Bourgault-Roy
4

Le code de votre exemple n'est pas facilement lisible car

  • Il mélange de nombreuses notions (Composition, Catalogues, Agrégats, Assemblages, ComposableParts ...) avec plusieurs niveaux d'imbrication alors que le code appelant ne devrait typiquement avoir à traiter qu'avec un seul niveau. Ressemble un peu à une violation de la loi de Déméter uniquement avec des appels de méthode imbriqués au lieu d'une chaîne de sous-sous-propriétés. Cela brouille quelque peu l'intention derrière la ligne de code.

  • Il y a une utilisation singleton étrange - AssemblySource.Instance.Select()implique que l'instance est un IEnumerable, ce qui est un peu gênant.

  • La variable x dans l'expression lambda est moche. En général, vous essayez de donner à vos variables lambda l'intention de révéler des noms - aide le lecteur à identifier de quel type de données il s'agit, même s'il ne connaît pas les lambdas.

  • On ne sait pas pourquoi vous devez filtrer avec OfType<T>()une collection d'objets que vous venez de mettre à jour ...

Cependant, tous ces défauts sont liés à la façon dont le développeur d'origine a écrit le code et à son expression expressive. Ce n'est pas quelque chose de spécifique aux dernières versions de C # et à l'apparition d'expressions lambda.

Plus généralement, cela aide si le lecteur de votre code sait comment fonctionnent les lambdas, mais avec des noms suffisamment clairs, vous parvenez presque toujours à rendre votre code lisible, même pour quelqu'un ayant une expérience antérieure à .NET 3.5.

guillaume31
la source
2

Chaque fois qu'une nouvelle version du langage populaire gagne de nouvelles constructions, des débats similaires se posent.

J'ai également eu ces doutes il y a des années (http://gsscoder.blogspot.it/2009/08/use-csharps-features-wisely.html).

À mon avis, C # évolue de manière élégante et cohérente.

Le code de votre exemple n'est pas complexe, vous n'êtes peut-être pas utilisé avec des expressions lamda.

Le problème est que C # n'est pas seulement un langage orienté objet, il prend désormais également en charge les constructions fonctionnelles (http://archive.msdn.microsoft.com/FunctionalCSharp/).

gsscoder
la source
1

Il m'a fallu un peu de temps pour comprendre la syntaxe Lambda, et je suis issu des mathématiques et de la physique. C'est un peu similaire aux expressions mathématiques, même si elles utilisent -> au lieu de =>:

Exemple mathématique f (x): = x-> x + 2 ceci se lit comme "La fonction f de x est définie comme x correspond à x + 2

exemple c # del myDelegate = x => x +2; cela se lit comme "myDelegate est une fonction telle que myDelegate (x) mappe x sur x + 2

L'incohérence entre les mathématiques et la programmation n'aide pas vraiment, bien que je suppose -> avait déjà été repris en C ++ comme "propriété de" (urrgghh!)

http://en.wikipedia.org/wiki/List_of_mathematical_symbols

Andy R
la source
"bien que je suppose -> avait déjà été repris en C ++ comme" propriété de "(urrgghh!)" Comme si! Mal en C ++, cela signifie "propriété de l'allocation dynamique ...". Si quelque chose n'est pas dynamique, vous utilisez la période comme dans toutes les autres langues.
mg30rg du