Warum Teilklassen verwenden?

72

Nach meinem Verständnis partialbewirkt das Schlüsselwort nichts anderes, als die Aufteilung einer Klasse auf mehrere Quelldateien. Gibt es einen Grund, dies anders als für die Code-Organisation zu tun? Ich habe gesehen, dass es dafür in generierten UI-Klassen verwendet wird.

Es scheint ein schlechter Grund zu sein, ein ganzes Keyword zu erstellen. Wenn eine Klasse groß genug ist, um mehrere Dateien zu benötigen, tut sie wahrscheinlich zu viel. Ich dachte, dass Sie es vielleicht verwenden könnten, um eine Klasse für einen anderen Programmierer teilweise zu definieren, aber es wäre besser, eine abstrakte Klasse zu erstellen.

Michael K
quelle
10
Es ist auch nicht wirklich ein "ganzes Schlüsselwort". Es ist ein kontextbezogenes Schlüsselwort . partialBedeutet nur etwas, wenn es vorher kommt class. Sie können es als Bezeichnername in anderen Teilen des Codes usw. verwenden.
Dean Harding
4
Wenn Sie sie verwenden und es nicht für generierten Code ist, hasse ich Sie. Nicht Sie persönlich, sondern Sie allgemein. Ich hasse es, Code durch Teilklassen zu jagen.
Steven Evers
3
Es ist nicht schwer, Code zu "jagen", alle Klassenmethoden erscheinen immer noch in der Dropdown-Liste in VS, unabhängig davon, welchen Teil des Teils Sie betrachten. Andere Code-Navigation funktioniert auch gut (entweder "clevere" R # -Navigation oder gute alte Shift-Strg-F
Steve
2
Es ist ein Missverständnis, dass sich Teilklassen in separaten Dateien befinden müssen.
Bart

Antworten:

110

Dies ist in jedem Szenario sehr nützlich, in dem ein Teil der Klasse von einem benutzerdefinierten Tool generiert wird, da Sie dem generierten Code benutzerdefinierte Logik hinzufügen können, ohne die generierte Klasse zu erben. Btw. Aus dem gleichen Grund gibt es auch Teilmethoden.

Es geht nicht nur um die Benutzeroberfläche, sondern auch um andere Technologien wie Linq-To-Sql oder Entity Framework.

Ladislav Mrnka
quelle
44
Außerdem können Sie Ihren Code unter Versionskontrolle halten, ohne den generierten Code der Versionskontrolle zu unterziehen.
Frank Shearar
3
Guter Punkt, daran habe ich noch nie gedacht. Ich habe es allerdings nur in der Praxis missbraucht gesehen. Eine Möglichkeit für (miese) Programmierer, die Dateigröße überschaubar zu halten.
Martin Wickman
1
Die besten Beispiele, die ich verwendet habe, sind: a) für Linq to SQL-Datenkontexte, in denen Sie die Klassen erweitern möchten; b) die von Webdiensten übergebenen Klassen zu erweitern. In keinem Fall ist dies ein Missbrauch oder ein Mittel, um die Dateigrößen überschaubar zu halten. Ich wäre radikal unglücklich, wenn ich es so sehen würde (und würde Schritte unternehmen ...)
Murph
3
WinForm-Klassen verwenden es, um alle Steuerelemente zum Erstellen von Designer-Code auszublenden (besonders wichtig, da der Designer den gesamten Code, der erstellt wird, zufällig neu anordnet, was zu großen Problemen beim Vergleichen und Beschuldigen führt). Wenn Sie mit XML arbeiten, kann dies das XSD-Tool Erstellen Sie erneut Teilklassen aus der Schemadatei, damit Sie Ihren Code von der automatisch generierten Sortierung trennen können.
Dan Neely
2
@MartinWickman nicht unbedingt mies. Es ist ein guter Ausgangspunkt für die Umgestaltung von Gott-Objekten in Legacy-Code. Versuchen Sie, die Landschaft aufzuräumen, indem Sie zuerst große Klassen in separate Dateien aufteilen. (Ich weiß, dass Ihr Kommentar sehr alt ist)
Konrad Morawski
26

Wie Sie sagen, wird es oft verwendet, um generierten Code zu trennen. Es hat oft nichts mit der Größe von Klassen / Dateien zu tun.

Der Vorteil des Trennens des generierten Codes liegt im Stil. Generierter Code kann ziemlich hässlich und unlesbar sein und würde viele Codierungsstandards (und StyleCop-Prüfungen) nicht erfüllen, aber das ist in Ordnung, niemand muss ihn lesen oder direkt warten. Wenn Sie es also in einer anderen Datei "verstecken", können Sie sich darauf konzentrieren, sicherzustellen, dass der Rest der Klasse dem Standard entspricht, die StyleCop-Prüfungen bestehen und so weiter.

Ein anderer Bereich, in dem ich es verwendet habe, ist der, in dem eine Klasse mehrere Schnittstellen implementiert. Es kann ganz nett sein, die Implementierung in separate Dateien zu unterteilen. Dies ist jedoch eher eine Frage der persönlichen Präferenz. Ich habe noch nie gesehen, dass Kodierungsstandards dies erfordern ( oder verhindern) dies.

Steve
quelle
9
Ich hätte nie gedacht, Klassenteilungen zum Trennen von Schnittstellenimplementierungen zu verwenden, das ist eine hervorragende Idee.
Mark Booth
Es geht um viel mehr als Stil. Die Entwickler eines Codegenerators müssten durch die Rahmen springen, damit Sie Code in einer vom Generator verwalteten Datei bearbeiten können, und selbst dann wäre dies problematisch. Der größte Vorteil für mich ist, dass Sie einen Platz zum Schreiben von Code erhalten, den der Generator nicht berühren kann. Dies ist ein ziemlich greifbarer Vorteil.
Daniel
19

Einer der Entwickler, bei dem ich arbeite, hat sich vor ein paar Wochen eine ziemlich gute Verwendung für sie ausgedacht, um riesige Gottklassen umzugestalten, die außer Kontrolle geraten sind und viele öffentliche Methoden haben: indem sie die logischen Funktionen der einzelnen Teile voneinander trennen Die Klasse in separate Teilklassen Sie können die Klasse physisch in die atomaren Einheiten unterteilen, die die Klassen sein sollen, ohne vorhandene Funktionen zu unterbrechen. So können Sie sehen, was gemeinsam ist und was nicht. In dieser ersten Phase können Sie die Partials dann einfacher in ihre eigenen unabhängigen Klassen aufteilen und in der gesamten Codebasis implementieren. Ich fand das eine schöne Idee.

Im Allgemeinen denke ich jedoch, dass sie nur verwendet werden sollten, um maschinengenerierte Klassen beim Schreiben von neuem Code zu erweitern.


quelle
1
Ich kann sehen, wie praktisch das sein könnte. Ob das eine Rechtfertigung dafür ist, in der Sprache zu sein oder nicht ... Ich weiß es nicht. Maschinengenerierter Code ist jedoch eine andere Geschichte.
Michael K
2
Ja, es ist keine Rechtfertigung dafür, in der Sprache zu sein, aber es ist eine interessante Art, sie zu verwenden.
18

Ich kann mir einige nützliche Szenarien vorstellen, in denen Teilweise sinnvoll sind. Die meisten davon benutze ich selbst in meinen Projekten:

  • So trennen Sie den von Tool / IDE / Designer generierten Code von dem von Ihnen verwalteten Code. Ein gutes Beispiel ist die Form.Designer.csDatei, die den vom Designer generierten Code für Windows Forms-Anwendungen enthält. Viele andere .NET-Formate verfügen auch über einen vom Tool generierten Code, der möglicherweise beim Erstellen Ihres Projekts neu generiert werden kann, sodass alle Ihre benutzerdefinierten Änderungen verworfen werden. Die Trennung hilft Ihnen dabei, Ihren Code und Änderungen vor solchen automatisierten Änderungen zu schützen.

  • Wenn Sie mehrere Schnittstellen mit viel Code in der Implementierung implementieren. Ich neige dazu , eine separate Teildatei für jede solche Schnittstelle zu verwenden, es so zu benennen: {Class}.{Interface}.cs. In der IDE ist leicht zu erkennen, welche Schnittstellen die {Class}implementieren und wie.

  • Wenn die Klasse eine oder mehrere verschachtelte Klassen enthalten muss , insbesondere mit genügend Code, um in einer separaten Datei abgelegt zu werden. Ich würde mich an das obige Muster halten und die {Class}.{NestedClass}.csNamenskonvention für jede verschachtelte Klasse verwenden. Eine ähnliche Praxis wird bereits in der Antwort von Virtlink erwähnt .

  • Wenn ich staticKlassen schreibe , die Erweiterungsmethoden enthalten . Es ist häufig der Fall, ähnliche Klassen oder Schnittstellen mit der gleichen Erweiterungsmethodenlogik auszustatten - wie zum Beispiel Reversebei generischen und nicht generischen Auflistungen. Ich würde alle Erweiterungsmethoden für eine einzelne Klasse oder Schnittstelle in einen separaten Teil der statischen Klasse setzen. Zum Beispiel hätte ich alle Erweiterungsmethoden für die IListSchnittstelle an einem Ort und die gleichen Methoden, die für IList<T>in einer anderen Datei sind. Ein anderer Ansatz wäre, die gleiche Methode (alle Überladungen) mit allen möglichen Klassen für den thisParameter in den gleichen Teil zu setzen - wie mit allenReverseImplementierungen in einer Datei. Es hängt davon ab, an welcher Stelle die Trennung in Bezug auf das Codevolumen oder auf einige interne Konventionen, die Sie oder Ihre Organisation möglicherweise einhalten, besser gerechtfertigt ist.

  • Ich verwende dies nicht, aber ich habe gesehen, dass einige C / C ++ - Leute den Ansatz mögen, den ich hier beschreiben werde: erstelle einen Teil für die Klasse mit nur teilweisen Methoden . Dies ähnelt der C / C ++ - Methode zum Definieren von Schnittstellen und zum Trennen der Methodendeklaration von der Implementierung.

  • Trennung durch rein logische Bedenken . Wenn Sie zufällig mit einer großen Klasse arbeiten, die mehr als eine logische Gruppe von Operationen in sich vereint, können Sie jeden logisch verwandten Code in einen separaten Teil trennen. Gewöhnlich ist das Vorhandensein solcher Klassen gegen das Prinzip der Trennung von Interessen, aber es wird häufig im wirklichen Leben beobachtet, insbesondere bei älteren Codebasen. Mitbenutzer Steve Evers hat sie in seiner Antwort auf diese Frage erwähnt und sie mit dem Namen god objects bezeichnet. Ich persönlich würde den Ansatz von Teilklassen verwenden, um den Code zu teilen, bevor ein Refactoring von wirklich großen Dateien stattfindet, um meine Arbeit zu vereinfachen und das Refactoring transparenter zu machen. Dies wird auch die Konflikte verringern, die ich möglicherweise verursachen werde, wenn Versionsverwaltungssysteme wie SVN beteiligt sind.

Ivaylo Slavov
quelle
14

Ich habe es von niemandem erwähnt gesehen: Ich partialsetze verschachtelte Klassen in ihre eigenen Dateien.

Alle meine Codedateien enthalten nur eine Klasse, Struktur, Schnittstelle oder Aufzählung. Es erleichtert das Auffinden der Definition für ein Objekt erheblich, wenn die Dateinamen den Namen des gesuchten Objekts anzeigen. Und da Visual Studio versucht, die Projektordner mit den Namespaces abzugleichen, sollten die Dateinamen mit den Klassen übereinstimmen.

Das bedeutet auch , dass eine Klasse NestedClassinnerhalb verschachtelt MyClasswird seine eigene Datei in meinen Projekten haben: MyClass.NestedClass.cs.

partial class MyClass
{
    private class NestedClass
    {
        // ...
    }
}
Daniel AA Pelsmaeker
quelle
1
Ich frage mich, warum man diese Antwort herabstimmen würde. Die erwähnte Praxis ist weder ein Anti-Muster, noch würde sie irgendwelche Probleme verursachen, von denen ich weiß.
Ivaylo Slavov
1
Hiermit wird auch das Problem behoben, dass Klassendateien klein gehalten werden (in Bezug auf Codezeilen), während die ordnungsgemäße Kapselung von Code, der zu einer inneren Klasse gehört, weiterhin möglich ist.
Redcalx
10

Mit Ausnahme des generierten Codes verwenden, habe ich nur immer sie in dem Bemühen verwendet , gesehen zu verschleiern Gott Objekte . Der Versuch, eine neue Codebasis zu verstehen oder durch mehrere Quelldateien zu navigieren, bei denen es sich um dasselbe Objekt handelt, ist soooo ärgerlich.

Wenn Sie also fragen, Why use partial classes?antworte ich: Wenn Sie keinen generierten Code verwenden, tun Sie das nicht.

Steven Evers
quelle
+1. Es ist das erste Mal, dass ich sehe, dass diese Art von Objekten als Namen (Gottobjekte) bezeichnet wird, und es ist sogar offiziell. Es ist nie zu spät, etwas Neues zu lernen.
Ivaylo Slavov
6

Code-Organisation ist der einzige Grund, aber es geht tiefer, als es auf den ersten Blick scheint. Wenn Sie Teilklassen haben, in denen Teile generiert werden, können Sie einfach:

  • Generieren Sie Code neu, ohne dass Sie manuelle Änderungen in den Klassen erkennen müssen, in die Sie schreiben (damit Sie diese Teile nicht überschreiben).
  • Ausschluss der generierten Teilklassen von Testabdeckung, Quellcodeverwaltung, Metriken usw.
  • Verwenden Sie generierten Code, ohne Sie zu zwingen, Ihre Vererbungsstrategie darauf abzustützen (Sie können weiterhin von anderen Klassen erben).
Deckard
quelle
1

Es gibt auch einige Stellen, an denen generierte / implizierte Klassen als Teilklassen deklariert werden. Wenn der Entwickler sie erweitern muss, haben sie vollen Zugriff, ohne die Vererbung und das Überschreiben der gesamten Klasse zu beeinträchtigen. Schauen Sie sich die generierten Klassen im temporären Bereich von asp.net an, nachdem Sie zum ersten Mal eine Webforms-Site unter IIS ausgeführt haben, um einige Beispiele zu finden.

Rechnung
quelle
1

Ich kann mir eine schmutzige Verwendung für sie vorstellen.

Angenommen, Sie haben einige Klassen, die eine gemeinsame Funktionalität benötigen, diese Funktionalität jedoch nicht in die darüber liegende Vererbungskette einfügen möchten. Oder Sie haben eine Reihe von Klassen, die eine gemeinsame Hilfsklasse verwenden.

Grundsätzlich möchten Sie eine Mehrfachvererbung durchführen, aber C # mag dies nicht. Sie verwenden also partial, um das zu erstellen, was in C / C ++ im Wesentlichen ein # include ist.

Ordnen Sie alle Funktionen einer Klasse zu oder verwenden Sie die Hilfsklasse. Kopieren Sie dann den Helfer X-mal und benennen Sie ihn in Teilklasse A, B, C um. und teilweise auf Ihre Klassen A, B, C setzen.

Haftungsausschluss: Ja, das ist böse. Tu es niemals - besonders wenn es andere Leute wütend auf dich macht.

Kennzeichen
quelle
Mixins. Dies könnte mit T4 kombiniert werden, um manuelles Kopieren zu vermeiden ...
Peter Taylor
Wie bereits erwähnt, ähnelt dies stark Mixins. Sie nähern sich jedoch einem Konzept, das als "Merkmale" bezeichnet wird, und sie werden sicher damit umgehen, was Sie erreichen möchten. Ich habe eine sprachunabhängige Beschreibung unter publius-ovidius.livejournal.com/314737.html. Ich habe nach einer sauberen Implementierung für C # gesucht, aber noch keine gesehen.
Ovid