Warum und wann sollte ich eine Klasse "statisch" machen? Was ist der Zweck des Schlüsselworts "static" für Klassen?

47

Das staticSchlüsselwort für ein Mitglied in vielen Sprachen bedeutet, dass Sie keine Instanz dieser Klasse erstellen sollten, um auf dieses Mitglied zugreifen zu können. Ich sehe jedoch keine Rechtfertigung dafür, eine ganze Klasse zu bilden static. Warum und wann sollte ich einen Kurs machen static?

Welche Vorteile bringt mir der Unterricht static? Ich meine, nachdem man eine statische Klasse deklariert hat, sollte man immer noch alle Mitglieder als statisch deklarieren, auf die er / sie ohne Instanziierung Zugriff haben möchte.

Dies bedeutet, dass die MathKlasse beispielsweise als normal (nicht statisch) deklariert werden kann, ohne dass sich dies auf den Code des Entwicklers auswirkt. Mit anderen Worten, eine Klasse statisch oder normal zu machen, ist für Entwickler irgendwie transparent.

Saeed Neamati
quelle
Wenn Sie im Func Prog-Stil nach Rich Hickey codieren, kann dies hilfreich sein.
Job
1
Meiner Meinung nach sind statische Klassen lediglich eine Krücke, über die C # versucht, einen massiven Konstruktionsfehler in der Sprache zu verbergen. Warum sollte man eine Sprache um diese unsinnige All-Muss-Klasse-Ideologie herum strukturieren, um dann zusätzliche Syntax einzuführen, damit man diese unnötige Einschränkung umgehen kann? Wenn C # gerade von Anfang an freie Funktionen zugelassen hätte, wären keine statischen Klassen erforderlich.
7.

Antworten:

32

Es macht den Benutzern klar, wie die Klasse verwendet wird. Zum Beispiel wäre es Unsinn, den folgenden Code zu schreiben:

Math m = new Math();

C # nicht muß dies verbieten , aber da es keinen Zweck dient, könnte genauso gut dem Benutzer sagen , dass. Einige Leute (einschließlich ich) halten an der Philosophie fest, dass Programmiersprachen (und APIs…) so restriktiv wie möglich sein sollten, damit sie nur schwer falsch verwendet werden können. Die einzigen zulässigen Operationen sind dann sinnvolle und (hoffentlich) korrekte.

Konrad Rudolph
quelle
3
Ich bin mit dieser Philosophie nicht einverstanden. Das Problem, das ich habe, ist, dass sich etwas ändern muss. Es mag zwar Sinn gemacht haben, es vorher einzuschränken, aber diese Einschränkung behindert unsere Entwicklung für zukünftige Bedürfnisse. Und die Legacy-Bibliothek, mit der wir zwangsläufig mit dem alten System kommunizieren müssen (das Sie geschrieben und alle Klassen versiegelt und statisiert haben), bietet uns keine Möglichkeit, den von Ihnen geschriebenen Code zu erweitern oder zu ändern. Nur weil Sie keinen Grund hatten, Ihre Klasse zu instanziieren, heißt das nicht, dass ich in Zukunft keinen Bedarf mehr dafür haben werde. Berücksichtigen Sie auch die Zukunft, während das Festlegen einer Utility-Klasse sinnvoll ist.
SoylentGray
19
@Chad Dann ist die Bibliothek schlecht gestaltet. Das Erweitern von Klassen, die nicht für die Erweiterung konzipiert wurden, funktioniert letztendlich nicht. Machen Sie die Klasse also sealedzuallererst. Ich gebe zu, dass nicht alle Menschen mit diesem Gefühl einverstanden sind, aber die Kommentare sind kein guter Ort, um darüber zu diskutieren. Aber im Falle staticdieses Problems stellt sich nicht einmal: Instanziieren System.Mathwird niemals Sinn machen.
Konrad Rudolph
1
@Konard, wenn Sie alle Member der MathKlasse als statisch deklarieren, ohne die Klasse als statisch zu deklarieren Math, können Sie sie dennoch verwenden, ohne dass eine Instanziierung erforderlich ist.
Saeed Neamati
11
@Saeed - Dies ist wahr, aber Sie können die Klasse auch instanziieren, was keinen Zweck erfüllt. Was mache ich mit einer Instanz der Math-Klasse? Um die Absicht der Klasse explizit anzuzeigen (keine Instanziierung erforderlich oder erwünscht), ist sie markiert static.
Corbin 26.
3
@ Saeed Lass uns das umkehren: Ich schlage vor, du liest meine Antwort noch einmal, da du sie anscheinend falsch verstanden hast: Deine Kommentare haben nichts mit meiner Antwort zu tun. Ich habe nie gesagt, dass Sie schreiben müssen new Math().
Konrad Rudolph
24

StackOverflow hat eine großartige Diskussion zu diesem Thema . Zum leichteren Nachschlagen kopiere ich es hier und füge es im Namen seines Autors Mark S. Rasmussen ein :

Ich habe meine Gedanken zu statischen Klassen in einem früheren Thread geschrieben :

Früher liebte ich Utility-Klassen, die mit statischen Methoden gefüllt waren. Sie haben eine große Konsolidierung von Hilfsmethoden vorgenommen, die sonst Redundanz und Wartungsaufwand verursachen würden. Sie sind sehr einfach zu bedienen, keine Instantiierung, keine Entsorgung, nur Feuer und Vergessen. Ich denke, dies war mein erster unbeabsichtigter Versuch, eine serviceorientierte Architektur zu schaffen - viele zustandslose Services, die nur ihren Job gemacht haben und sonst nichts. Während ein System wächst, kommen jedoch Drachen.

Polymorphismus

Nehmen wir an, wir haben die Methode UtilityClass.SomeMethod, die glücklich mitschwirrt. Plötzlich müssen wir die Funktionalität etwas ändern. Der größte Teil der Funktionalität ist derselbe, aber wir müssen trotzdem einige Teile ändern. Wäre es keine statische Methode gewesen, könnten wir eine Derivate-Klasse erstellen und den Methodeninhalt nach Bedarf ändern. Da es eine statische Methode ist, können wir nicht. Klar, wenn wir nur Funktionalität vor oder nach der alten Methode hinzufügen müssen, können wir eine neue Klasse erstellen und die alte in ihr aufrufen - aber das ist nur eklatant.

Schnittstellenprobleme

Statische Methoden können aus logischen Gründen nicht über Schnittstellen definiert werden. Und da wir statische Methoden nicht überschreiben können, sind statische Klassen nutzlos, wenn wir sie über ihre Schnittstelle weitergeben müssen. Dies macht es uns unmöglich, statische Klassen als Teil eines Strategiemusters zu verwenden. Wir können einige Probleme beheben, indem wir Delegaten anstelle von Schnittstellen übergeben.

Testen

Dies geht im Wesentlichen mit den oben erwähnten Problemen der Benutzeroberfläche einher. Da wir nur sehr eingeschränkte Möglichkeiten haben, Implementierungen auszutauschen, können wir auch den Produktionscode nicht durch Testcode ersetzen. Wir können sie wieder einpacken, müssen jedoch große Teile unseres Codes ändern, um Wrapper anstelle der eigentlichen Objekte akzeptieren zu können.

Fördert Blobs

Da statische Methoden in der Regel als Dienstprogrammmethoden verwendet werden und Dienstprogrammmethoden in der Regel unterschiedliche Zwecke haben, erhalten wir schnell eine große Klasse mit nicht kohärenter Funktionalität - im Idealfall sollte jede Klasse einen einzigen Zweck innerhalb des Systems haben. Ich hätte viel lieber eine fünffache Klasse, solange ihre Ziele klar definiert sind.

Parameter Kriechen

Zunächst könnte diese kleine nette und unschuldige statische Methode einen einzigen Parameter annehmen. Mit zunehmender Funktionalität werden einige neue Parameter hinzugefügt. Bald werden weitere Parameter hinzugefügt, die optional sind, sodass wir Überladungen der Methode erstellen (oder einfach Standardwerte in Sprachen hinzufügen, die diese unterstützen). In Kürze haben wir eine Methode, die 10 Parameter akzeptiert. Nur die ersten drei sind wirklich erforderlich, Parameter 4-7 sind optional. Wenn jedoch Parameter 6 angegeben wird, müssen auch 7-9 ausgefüllt werden. Hätten wir eine Klasse mit dem einzigen Zweck erstellt, das zu tun, was diese statische Methode getan hat, könnten wir dies lösen, indem wir die erforderlichen Parameter in das Feld eingeben Der Benutzer kann optionale Werte über Eigenschaften oder Methoden festlegen, um mehrere voneinander abhängige Werte gleichzeitig festzulegen. Ebenfalls,

Fordern Sie die Konsumenten auf, ohne Grund eine Instanz von Klassen zu erstellen

Eines der häufigsten Argumente ist, warum von Verbrauchern unserer Klasse verlangt wird, eine Instanz zum Aufrufen dieser einzelnen Methode zu erstellen, ohne die Instanz danach zu verwenden. Das Erstellen einer Instanz einer Klasse ist in den meisten Sprachen eine sehr, sehr kostengünstige Operation, daher ist Geschwindigkeit kein Problem. Das Hinzufügen einer zusätzlichen Codezeile für den Verbraucher ist mit geringen Kosten verbunden, um den Grundstein für eine in Zukunft viel wartbarere Lösung zu legen. Wenn Sie keine Instanzen erstellen möchten, erstellen Sie einfach einen Singleton-Wrapper für Ihre Klasse, der eine einfache Wiederverwendung ermöglicht. Dies setzt jedoch voraus, dass Ihre Klasse statusfrei ist. Wenn es nicht zustandslos ist, können Sie dennoch statische Wrapper-Methoden erstellen, die alles verarbeiten und Ihnen auf lange Sicht dennoch alle Vorteile bieten. Endlich,

Nur ein Sith handelt absolut

Natürlich gibt es Ausnahmen von meiner Abneigung gegen statische Methoden. Echte Utility-Klassen, die kein Aufblähungsrisiko darstellen, eignen sich hervorragend für statische Methoden - am Beispiel von System.Convert. Wenn es sich bei Ihrem Projekt um ein Einzelprojekt handelt, für das keine zukünftigen Wartungsarbeiten erforderlich sind, spielt die Gesamtarchitektur keine große Rolle - statisch oder nicht statisch, spielt jedoch keine Rolle - die Entwicklungsgeschwindigkeit.

Standards, Standards, Standards!

Die Verwendung von Instanzmethoden hindert Sie nicht daran, auch statische Methoden zu verwenden und umgekehrt. Solange die Unterscheidung begründet und standardisiert ist. Es gibt nichts Schlimmeres, als einen Blick auf eine Business-Schicht zu werfen, die sich über verschiedene Implementierungsmethoden erstreckt.

Rick
quelle
12
Hmmm, ich weiß nicht, dass ich hier eine vollständige Antwort kopieren und einfügen würde. Vielleicht verlinken und umschreiben, einen alternativen Standpunkt einbeziehen, da Sie eine "Diskussion" erwähnen, Ihre eigenen Erfahrungen teilen?
Corbin 26.
2
"Nur ein Sith handelt in absoluten Zahlen" - so etwas habe ich noch nie gesehen ... aber es ist PERFEKT. Hat mich zum Lachen gebracht.
Brook
4
@Corbin: Mark hatte eine tolle Antwort. Ich gab ihm Kredit und ich kopierte / fügte zur Erleichterung der Referenz. Meine eigenen Ansichten zu diesem Thema stimmen mit denen von Mark überein. Ich verstehe den Sinn Ihres Kommentars nicht, außer um eine Debatte zu beginnen.
Rick
1
@Rick: Die kopierte Antwort ist ziemlich lang und es würde definitiv helfen, sie zu paraphrasieren. Nur ein Satz wie "Es ist nicht nur so, dass sie nicht wirklich nützlich sind, sie können sogar schaden, wie dieser SO-Beitrag zeigt:" würde mich schnell darüber hinweggehen lassen, da ich sehe, dass es nicht die Informationen sind, nach denen ich gesucht habe.
blubb
@ Simon: LOL I 1 + 'ed deinen Beitrag. In dem Versuch aufzuhören, über etwas so Dummes zu streiten, stimme ich einfach zu. :) Hoffentlich fand das Originalposter meine Antwort / Kopieren und Einfügen nützlich.
Rick
16

Ich bin überrascht, dass noch niemand erwähnt hat, dass staticKlassen Erweiterungsmethoden zulassen - sicheres Erweitern eines vorhandenen Typs (einschließlich Hinzufügen von Methodendefinitionen zu Schnittstellen ), die Sie nicht besitzen. Zum Beispiel, was in Scala ist

trait MyFoo {
  def foo: Int
  def plusFoo(a: Int) = foo + a
}

kann in C # ausgedrückt werden als

public interface IMyFoo {
  int Foo();
}

public static class MyFooExtensions {
  public static int PlusFoo(this IMyFoo f, int a) {
    return f.Foo() + a;
  }
}
Frank Shearar
quelle
Du hast Nadel im Heuhaufen gefunden. +1
Saeed Neamati
3

Für mich ist eine statische Klasse (in C #) so etwas wie das Aufrufen einer Funktion.

Zum Beispiel

public static string DoSomething(string DoSomethingWithThisString){}

Ich gebe eine Schnur ein und hole eine Schnur zurück. Alles ist in dieser Methode enthalten. Kein Zugriff auf Mitgliedsvariable usw.

Um ehrlich zu sein, war es für mich hauptsächlich nützlich, die Codezeilen zu verringern:

Warum machen:

MyClass class = new MyClass();
String s = class.DoSomething("test");

Wenn ich das kann:

String s = MyClass.DoSomething("test");

In diesen Fällen würde ich statische Klassen verwenden, wenn ich etwas tun wollte, ohne eine Klasse zu benötigen, und weniger Tippen bedeuten würde.

Mark S.-Punkte sind gültig, aber für mich ist es einfacher, wenn ich einige Dienstprogrammmethoden habe, so zu tun, als würde ich eine Funktion aufrufen, um sie als Einzeiler zu referenzieren.

Das mag vereinfachend sein, aber so habe ich es meistens benutzt static.

Jon Raynor
quelle
1
new MyClass (). DoSomething ("test") ist noch gültig. Ich benutze dies oft für Test Data Builder.
JoanComasFdz