Ich bin sehr neugierig auf Gedanken und Best Practices der Branche in Bezug auf statische Mitglieder oder ganze statische Klassen. Gibt es irgendwelche Nachteile oder nimmt es an irgendwelchen Anti-Mustern teil?
Ich sehe diese Entitäten als "Dienstprogrammklassen / Mitglieder" in dem Sinne, dass sie keine Instanziierung der Klasse erfordern oder verlangen, um die Funktionalität bereitzustellen.
Was sind die allgemeinen Gedanken und Best Practices der Branche dazu?
In den folgenden Beispielklassen und Mitgliedern wird veranschaulicht, worauf ich mich beziehe.
// non-static class with static members
//
public class Class1
{
// ... other non-static members ...
public static string GetSomeString()
{
// do something
}
}
// static class
//
public static class Class2
{
// ... other static members ...
public static string GetSomeString()
{
// do something
}
}
Danke im Voraus!
c#
class-design
Thomas Stringer
quelle
quelle
Antworten:
Vermeiden Sie im Allgemeinen statische Aufladungen - insbesondere statische Zustände.
Warum?
Statik verursacht Probleme mit der Parallelität. Da es nur eine Instanz gibt, wird diese natürlich von der gleichzeitigen Ausführung gemeinsam genutzt. Diese gemeinsam genutzten Ressourcen sind der Fluch der gleichzeitigen Programmierung und oft nicht müssen geteilt werden.
Statik verursacht Probleme beim Testen von Einheiten. Jedes Unit-Test-Framework, das es wert ist, wird gleichzeitig getestet. Dann stößt du auf # 1. Schlimmer noch, Sie komplizieren Ihre Tests mit all dem Setup / Teardown-Zeug und dem Hackery, in das Sie geraten, um zu versuchen, Setup-Code zu "teilen".
Es gibt auch viele kleine Dinge. Statik ist in der Regel unflexibel. Sie können sie nicht verbinden, Sie können sie nicht überschreiben, Sie können das Timing ihrer Konstruktion nicht steuern, Sie können sie in Generika nicht gut verwenden. Sie können sie nicht wirklich versionieren.
Statik wird sicherlich verwendet: Konstante Werte funktionieren hervorragend. Reine Methoden, die nicht in eine einzelne Klasse passen, können hier hervorragend funktionieren.
Aber im Allgemeinen vermeiden Sie sie.
quelle
volatile
. Sie erhalten nur keine Zusicherungen aus dem Speichermodell ohne flüchtige Daten, sodass das Ändern einer Variablen in einem Thread möglicherweise nicht sofort oder überhaupt nicht berücksichtigt wird.Wenn die Funktion "rein" ist, sehe ich keine Probleme. Eine reine Funktion arbeitet nur in den Eingabeparametern und liefert darauf basierend ein Ergebnis. Es hängt nicht von einem globalen Zustand oder einem externen Kontext ab.
Wenn ich mir Ihr eigenes Codebeispiel ansehe:
Diese Funktion akzeptiert keine Parameter. Daher ist es wahrscheinlich nicht rein (die einzige reine Implementierung dieser Funktion wäre die Rückgabe einer Konstanten). Ich gehe davon aus, dass dieses Beispiel nicht repräsentativ für Ihr eigentliches Problem ist. Ich weise lediglich darauf hin, dass dies wahrscheinlich keine reine Funktion ist.
Nehmen wir ein anderes Beispiel:
Es ist nichts Falsches daran, dass diese Funktion statisch ist. Wir könnten dies sogar zu einer Erweiterungsfunktion machen, damit der Client-Code noch besser lesbar wird. Erweiterungsfunktionen sind im Grunde nur eine spezielle Art von statischen Funktionen.
Telastyn erwähnt die Parallelität korrekt als potenzielles Problem mit statischen Mitgliedern. Da diese Funktion jedoch keinen gemeinsam genutzten Status verwendet, treten hier keine Parallelitätsprobleme auf. Tausend Threads können diese Funktion gleichzeitig ohne Parallelitätsprobleme aufrufen.
Im .NET Framework gibt es seit geraumer Zeit Erweiterungsmethoden. LINQ enthält viele Erweiterungsfunktionen (z. B. Enumerable.Where () , Enumerable.First () , Enumerable.Single () usw.). Wir sehen das nicht als schlecht an, oder?
Unit-Tests können häufig von Vorteil sein, wenn der Code austauschbare Abstraktionen verwendet, sodass der Unit-Test den Systemcode durch ein Test-Double ersetzen kann. Statische Funktionen verbieten diese Flexibilität, dies ist jedoch vor allem an den Grenzen der Architekturschicht wichtig, wo wir beispielsweise eine tatsächliche Datenzugriffsschicht durch eine gefälschte Datenzugriffsschicht ersetzen möchten .
Wenn wir jedoch einen Test für ein Objekt schreiben, das sich anders verhält, je nachdem, ob eine Zahl ungerade oder gerade ist, müssen wir die Funktion nicht wirklich
IsOdd()
durch eine alternative Implementierung ersetzen können . Ebenso sehe ich nicht, wann wir zumEnumerable.Where()
Testen eine andere Implementierung bereitstellen müssen .Untersuchen wir also die Lesbarkeit des Client-Codes für diese Funktion:
Option a (mit der als Erweiterungsmethode deklarierten Funktion):
Option b:
Die statische (Erweiterungs-) Funktion macht den ersten Code besser lesbar, und die Lesbarkeit ist sehr wichtig. Verwenden Sie daher gegebenenfalls statische Funktionen.
quelle