Wird es schwieriger, C # zu lesen?

15

Im Verlauf von C # wurden viele Sprachfunktionen hinzugefügt. Es ist an einem Punkt angelangt, an dem es für mich unlesbar wird.

Als Beispiel betrachten wir den folgenden Code Schnipsel aus Caliburn.Micro Code hier :

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

Dies ist nur ein kleines Beispiel.

Ich habe eine Reihe von Fragen:

  • Ist das ein häufiges oder bekanntes Problem?
  • Findet die C # -Community dasselbe?
  • Ist dies ein Problem mit der Sprache oder ist es der vom Entwickler verwendete Stil?

Gibt es einfache Lösungen, um den Code anderer besser zu verstehen und das Schreiben von Code auf diese Weise zu vermeiden?

Steven Evers
quelle
6
Es ist die Lambda-Funktion, sie sind schwer zu lesen, bis man sie wirklich versteht.
Ryathal
9
Ich denke, Sie kannten die Syntax wirklich nicht so gut, wie Sie dachten. In der Praxis ist das Lesen Ihres Beispiels einfach.
ChaosPandion
6
@ Thomas Owens: Holy shit man, gib uns wenigstens etwas Zeit, um Fragen zu bearbeiten, damit sie offen bleiben. 15 Minuten? Komm jetzt.
Steven Evers
4
@DannyVarod: 1. "Nein" ist keine akzeptable Antwort. 2. Die Frage ist geschlossen. 3. Wenn Sie der Meinung sind, dass die Frage nicht hier sein sollte, markieren / stimmen Sie ab, um sie zu schließen oder zu bearbeiten, um sie zu verbessern .
Steven Evers
2
@ Thorbjørn Ravn Andersen - Was kann man aus einer Ohrfeige lernen? Dies enthält keine Informationen!

Antworten:

12

Kurzer Hinweis, wo sich die Sprache befindet: C # ist eine Allzweck-Programmiersprache . im Gegensatz zu C (wie C ++) strebt es eine hohe Abstraktion an; Im Gegensatz zu Lisp-Dialekten strebt es nach praktischer Ausdruckskraft, und im Gegensatz zu Java wird es aggressiver betrieben - Microsoft reagiert schnell auf die Nachfrage.

Aus diesem Grund verwandelt es sich in eine Mischung aus LINQ, Lambdas und seltsamen neuen Schlüsselwörtern - es passt sich schnell an neue Problemdomänen an, und dies ist in der Tat ein schlüpfriger Hang zu einer Sprache, die so komplex ist, dass nur sehr wenige sie richtig verwenden können (wie C ++). Es ist kein Problem mit C # selbst, es ist ein Problem mit jeder Sprache mit diesen ehrgeizigen Zielen.

Die Community ist sich dessen bewusst und, was noch wichtiger ist, die Jungs hinter C # sind sich dessen sehr bewusst (die wenigen Blogeinträge und Podcasts zu den Neuerungen in C # 5.0 zeigen, wie sehr diese Jungs die Dinge einfach halten wollen). Microsoft wird versuchen , einen Teil der Last von ihrem Flaggschiff zu nehmen , so dass es kein tarpit nicht geworden: die Einführung des DLR, einem hellen Scheinwerfer über neue Sprachen (F #).

Darüber hinaus werden in Kursmaterialien zu C # (einschließlich MS-Zertifizierungen) je nach Problem unterschiedliche (aber konsistente) Verwendungsweisen für C # empfohlen. Wählen Sie Ihre Waffe aus und halten Sie sich daran: LINQ in flüssigem Stil bei einigen Problemen, Lambdas mit TPL bei anderen, normal -alt für die meisten.

vski
quelle
2
Können Sie erklären, was Sie mit "im Gegensatz zu Lisp Dialekten, die auf praktische Ausdruckskraft abzielen" meinen?
Giorgio
27

Nein. C # gibt uns mehr Möglichkeiten, um Code prägnanter zu schreiben. Dies kann genutzt oder missbraucht werden. Bei richtiger Verwendung ist der Code leichter zu lesen. Bei unsachgemäßer Verwendung kann der Code schlechter lesbar sein. Schlechte Programmierer hatten jedoch immer die Fähigkeit, schwer lesbaren Code zu schreiben.

Nehmen wir zwei Beispiele, die beide auf Beispielen basieren, die in StackOverflow für dasselbe Problem gefunden wurden und Typen mit einem bestimmten Attribut finden:

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;
}

Meiner Meinung nach erleichtert die neue Syntax das Lesen erheblich.

Pete
quelle
Die neue Syntax ist zwar leichter zu lesen, aber leichter zu verstehen, wie was unter der Haube vor sich geht? Das erste Beispiel ist verständlich, das zweite ist besser lesbar.
Nawfal
4
@nawfal Nach meiner Erfahrung haben die Leute viel mehr Probleme, die Konstrukte "yield return" zu verstehen als linq-Anweisungen. daher würde ich argumentieren, dass die zweite lesbarer und verständlicher ist. Weder sagt Ihnen wirklich, was unter der Haube vor sich geht, da beide Abstraktionen zugeordnet sind, die weit davon entfernt sind, wie Prozessoren tatsächlich funktionieren.
Chuu
Um ehrlich zu sein, mag ich LINQ nicht, weil es Schleifen entfernt und Codierer dazu ermutigt, Daten in separaten LINQ-Abfragen (mit separaten Schleifen im Hintergrund) anstatt in einer Schleife mit einem komplizierteren Kern abzurufen.
mg30rg
8

Als LINQ hinzugefügt wurde, verbreitete es einen Codierungsstil, bei dem viele Methoden im Fluent-Stil verkettet und Lambdas als Parameter übergeben wurden.

Dieser Stil ist sehr kraftvoll, wenn Sie sich damit wohl fühlen. Allerdings kann es machen ziemlich unlesbaren Code missbraucht werden. Ich denke nicht, dass Ihr Beispiel zu schlecht ist, obwohl der Einzug eher zufällig ist (und das ist eine übliche Eigenschaft dieses Codestils, Visual Studio zieht ihn nicht sehr konsistent automatisch ein).

An meinem Arbeitsplatz haben wir diesen Codierungsstil bei der Überprüfung unserer Codierungsstandards zu Beginn dieses Jahres erörtert und beschlossen, die Entwickler zu ermutigen, den Code so aufzubrechen: Insbesondere, keine anonymen Objekte innerhalb eines Funktionsaufrufs zu erstellen und zu initialisieren. So würde Ihr Code werden:

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);
Carson63000
quelle
1
Ist es nicht seltsam, einen neuen Typ zu erstellen und diesen anschließend dynamisch einzuschalten?
DeadMG
Wahrscheinlich habe ich das Code-Snippet nur zerlegt, ohne wirklich darüber nachzudenken, was seine Absicht war. Ich wollte nur alle Instantiierungen in eigene Codezeilen aufteilen, um zu zeigen, welche Auswirkungen dies auf das Erscheinungsbild des Codes haben würde.
Carson63000
Ich übernehme die Schuld für den Einzug :(). Hier ist das Original: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/…
1
Ich stimme dieser Antwort voll und ganz zu. Das Problem im Beispielcode ist nicht die neue C # -Syntax, sondern, dass der Schreiber versucht hat, mit einem "Einzeiler" -Code clever umzugehen. Es gibt diese alte Regel aus UNIX: Alles sollte nur eines tun und es auch gut machen. Dies gilt für Klassen, für Methoden und natürlich für Codezeilen.
Laurent Bourgault-Roy
4

Der Code in Ihrem Beispiel ist deshalb nicht leicht lesbar

  • Es mischt viele Begriffe (Komposition, Kataloge, Aggregate, Baugruppen, ComposableParts ...) mit mehreren Verschachtelungsebenen, während der aufrufende Code normalerweise nur eine Ebene haben sollte. Sieht ein bisschen aus wie eine Verletzung des Gesetzes von Demeter, nur mit verschachtelten Methodenaufrufen anstelle einer Kette von Sub-Sub-Eigenschaften. Dadurch wird die Absicht hinter der Codezeile etwas durcheinander gebracht.

  • Es gibt eine seltsame Singleton-Verwendung - AssemblySource.Instance.Select()impliziert, dass Instance eine IEnumerable ist, was etwas umständlich ist.

  • Die Variable x im Lambda-Ausdruck ist hässlich. Im Allgemeinen versuchen Sie, Ihren Lambda-Variablen Namen zu geben, die Aufschluss darüber geben, um welche Art von Daten es sich bei der Funktion handelt, auch wenn er nicht mit Lambdas vertraut ist.

  • Es ist nicht klar, warum Sie mit OfType<T>()einer Sammlung von Objekten filtern sollten, die Sie gerade neu erstellt haben ...

All diese Mängel hängen jedoch damit zusammen, wie der ursprüngliche Entwickler den Code geschrieben hat und wie ausdrucksstark er ihn gestaltet hat. Dies ist nicht spezifisch für die neuesten Versionen von C # und das Auftreten von Lambda-Ausdrücken.

Im Allgemeinen ist es hilfreich, wenn der Leser Ihres Codes weiß, wie Lambdas funktionieren, aber mit einer eindeutigen Benennung schaffen Sie es fast immer, Ihren Code auch für jemanden mit einem Hintergrund vor .NET 3.5 lesbar zu machen.

guillaume31
quelle
2

Immer wenn eine neue Version der populären Sprache neue Konstrukte gewinnt, entstehen ähnliche Debatten.

Ich hatte auch diese Zweifel vor Jahren (http://gsscoder.blogspot.it/2009/08/use-csharps-features-wisely.html).

Meiner Meinung nach entwickelt sich C # auf elegante und konsistente Weise.

Der Code in Ihrem Beispiel ist nicht komplex. Vielleicht werden Sie nicht mit Lamda-Ausdrücken verwendet.

Das Problem ist, dass C # nicht nur eine objektorientierte Sprache ist, sondern jetzt auch funktionale Konstrukte unterstützt (http://archive.msdn.microsoft.com/FunctionalCSharp/).

gsscoder
quelle
1

Ich habe eine Weile gebraucht, um die Lambda-Syntax zu verstehen, und ich habe einen mathematischen und physischen Hintergrund. Es ist ein bisschen ähnlich wie mathematische Ausdrücke, obwohl sie -> anstelle von => verwenden:

Das mathematische Beispiel f (x): = x-> x + 2 lautet wie folgt: "Die Funktion f von x ist definiert als x bildet auf x + 2 ab

c # example del myDelegate = x => x +2; dies lautet wie folgt: "myDelegate ist eine Funktion, so dass myDelegate (x) x auf x + 2 abbildet

Die Inkonsistenz zwischen Mathematik und Programmierung hilft nicht wirklich, obwohl ich nehme an -> bereits in C ++ als "Eigenschaft von" aufgenommen wurde (urrgghh!)

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

Andy R
quelle
"obwohl ich nehme an -> wurde bereits in C ++ als" Eigentum von "aufgenommen (urrgghh!)" Als ob! Schlecht in C ++ bedeutet es "Eigenschaft des dynamisch zugewiesenen ...". Wenn etwas nicht dynamisch ist, verwenden Sie den Punkt wie in jeder anderen Sprache.
mg30rg