Dies ist eine sehr gute Frage, da ich eine Reihe von C ++ - Entwicklern gesehen habe, die schrecklichen C # -Code geschrieben haben.
Es ist am besten, sich C # nicht als "C ++ mit besserer Syntax" vorzustellen. Es sind verschiedene Sprachen, die unterschiedliche Denkansätze erfordern. C ++ zwingt Sie, ständig darüber nachzudenken, was die CPU und der Arbeitsspeicher tun werden. C # ist nicht so. C # ist speziell so konzipiert, dass Sie nicht an die CPU und den Arbeitsspeicher denken, sondern an die Geschäftsdomäne, für die Sie schreiben .
Ein Beispiel dafür, das ich gesehen habe, ist, dass viele C ++ - Entwickler gern for-Schleifen verwenden, weil sie schneller sind als foreach. Dies ist in C # normalerweise eine schlechte Idee, da dadurch die möglichen Typen der Auflistung eingeschränkt werden, über die iteriert wird (und daher die Wiederverwendbarkeit und Flexibilität des Codes).
Ich denke, dass der beste Weg, um von C ++ auf C # umzustellen, darin besteht, zu versuchen, das Codieren aus einer anderen Perspektive zu betrachten. Zunächst wird dies schwierig, da Sie im Laufe der Jahre völlig daran gewöhnt sein werden, den von Ihnen geschriebenen Code mithilfe des Threads "Was macht CPU und Speicher?" Im Gehirn zu filtern. In C # sollten Sie stattdessen über die Beziehungen zwischen den Objekten in der Geschäftsdomäne nachdenken. "Was möchte ich tun" im Gegensatz zu "Was macht der Computer?"
Wenn Sie mit einer Liste von Objekten etwas anfangen möchten, anstatt eine for-Schleife in die Liste zu schreiben, erstellen Sie eine Methode, die IEnumerable<MyObject>
eine foreach
Schleife verwendet.
Ihre konkreten Beispiele:
Steuern der Lebensdauer von Ressourcen, die eine deterministische Bereinigung erfordern (z. B. Dateien). Dies ist einfach in der Hand zu haben, aber wie man es richtig benutzt, wenn der Besitz der Ressource übertragen wird [... zwischen Threads]? In C ++ verwende ich einfach gemeinsame Zeiger und lasse es zur richtigen Zeit für die 'Garbage Collection' sorgen.
Sie sollten dies niemals in C # tun (insbesondere zwischen Threads). Wenn Sie etwas mit einer Datei tun müssen, tun Sie dies jeweils an einem Ort. Es ist in Ordnung (und in der Tat eine gute Praxis), eine Wrapper-Klasse zu schreiben, die die nicht verwalteten Ressourcen verwaltet, die zwischen Ihren Klassen übertragen werden. Versuchen Sie jedoch nicht, eine Datei zwischen Threads zu übertragen, und lassen Sie separate Klassen schreiben / lesen / schließen / öffnen. Teilen Sie nicht das Eigentum an nicht verwalteten Ressourcen. Verwenden Sie das Dispose
Muster, um mit der Bereinigung umzugehen.
Ständiges Kämpfen mit übergeordneten Funktionen für bestimmte Generika (ich mag Dinge wie teilweise Template-Spezialisierung in C ++). Sollte ich irgendwelche Versuche, eine generische Programmierung in C # durchzuführen, einfach abbrechen? Vielleicht sind Generika absichtlich eingeschränkt und es ist nicht C # -isch, sie zu verwenden, außer für einen bestimmten Bereich von Problemen?
Generika in C # sind so konzipiert, dass sie generisch sind. Spezialisierungen von Generika sollten von abgeleiteten Klassen behandelt werden. Warum sollte sich ein List<T>
Mensch anders verhalten, wenn es ein List<int>
oder ein ist List<string>
? Alle Operationen für List<T>
sind generisch und gelten für alle List<T>
. Wenn Sie das Verhalten der .Add
Methode auf a ändern möchten List<string>
, erstellen Sie eine abgeleitete Klasse MySpecializedStringCollection : List<string>
oder eine zusammengesetzte Klasse, MySpecializedStringCollection : IList<string>
die den generischen Wert intern verwendet, die Dinge jedoch auf andere Weise ausführt. Dies hilft Ihnen zu vermeiden, dass Sie das Liskov-Substitutionsprinzip verletzen und andere, die Ihre Klasse nutzen, auf königliche Weise verarschen.
Makrofunktion. Während dies im Allgemeinen eine schlechte Idee ist, gibt es für einige Problembereiche keine andere Problemumgehung (z. B. bedingte Auswertung einer Anweisung, wie bei Protokollen, die nur für Debug-Releases verwendet werden sollen). Wenn ich sie nicht habe, muss ich mehr if (condition) {...} Boilerplate einsetzen, und es ist immer noch nicht gleich, was die Auslösung von Nebenwirkungen angeht.
Wie bereits erwähnt, können Sie dazu Präprozessorbefehle verwenden. Attribute sind noch besser. Im Allgemeinen sind Attribute der beste Weg, um mit Dingen umzugehen, die keine "Kern" -Funktionalität für eine Klasse sind.
Kurz gesagt, denken Sie beim Schreiben von C # daran, dass Sie sich ausschließlich mit der Geschäftsdomäne befassen sollten und nicht mit der CPU und dem Arbeitsspeicher. Sie können später bei Bedarf optimieren , aber Ihr Code sollte die Beziehungen zwischen den Geschäftsprinzipien widerspiegeln, die Sie abbilden möchten.
C#
zum Generieren von anderem Code verwenden. Sie können eineCSV
oder eineXML
oder welche Datei als Eingabe lesen und eineC#
oder eineSQL
Datei generieren . Dies kann leistungsfähiger sein als die Verwendung von Funktionsmakros.