Grundlegendes zum statischen Schlüsselwort

15

Ich habe einige Erfahrung in der Entwicklung mit Java, Javascript und PHP.

Ich lese Schritt für Schritt Microsoft Visual C # 2010, was meines Erachtens ein sehr gutes Buch zur Einführung in die C # -Sprache ist.

Ich habe offenbar Probleme, das statische Schlüsselwort zu verstehen. Soweit ich weiß, müssen alle Methoden und Variablen statisch sein, wenn eine Klasse als statisch deklariert wird. Die Hauptmethode ist immer eine statische Methode, sodass in der Klasse die Hauptmethode vorhanden ist und alle Variablen und Methoden als statisch deklariert werden, wenn Sie sie in der Hauptmethode aufrufen müssen. Ich habe auch festgestellt, dass Sie zum Aufrufen einer statischen Methode aus einer anderen Klasse kein Objekt erstellen müssen, für das Sie den Klassennamen verwenden können.

Aber was ist der eigentliche Zweck des statischen Schlüsselworts? Wann sollte ich statische Variablen und Methoden deklarieren?

Nistor Alexandru
quelle
4
static in C # ist fast dasselbe wie static in Java. Wenn Sie es in Java verstehen, dürfen Sie in C #
superM
Java war meine erste Programmiersprache und ich habe dieses Konzept dort auch nicht verstanden. Ich habe Java nur für einen kurzen Zeitraum verwendet
Nistor Alexandru
Kurz gesagt: Verwenden Sie "static", wenn Sie keine Objektorientierung benötigen, z. B. nur einige eigenständige Methoden oder Variablen. Wenn eine Klasse als statisch deklariert wird, bedeutet dies, dass diese nicht objektorientierten Funktionen und Variablen nur in einem gemeinsamen Namen (Leerzeichen), dem Klassennamen, abgelegt werden.
Doc Brown

Antworten:

14

Das Schlüsselwort 'static' in C # bezieht sich auf etwas in der Klasse oder auf die Klasse selbst, die von allen Instanzen der Klasse gemeinsam genutzt wird. Beispielsweise kann von allen Instanzen dieser Klasse über den Klassennamen auf ein Feld zugegriffen werden, das als statisch markiert ist.

public class SomeObject
{
    //Static Field
    static int Foo = 3;

    //instance field
    private int _Foo2 = 4;

    //instance property
    public int Foo2{get{return _Foo2;}set{_Foo2 = value;}}


    //static factory method
    public static SomeObject CreateSomeObject(int fooValue)
    {
        SomeObject retVal = new SomeObject();
        retVal.Foo2 = fooValue;
        return retVal;
    }

    //Parameterless instance constructor
    public SomeObject()
    {
    }

    public static int Add(int x)
    {
        //Static methods can only deal with local variables, or fields that
        //  are also static in the class.  This one adds x to the static member foo
        return x + Foo;

        //Foo2 is not accessable here!
    }

      //Instance method
    public int AddSomething(int x)
    {
        //Add x to the property value of Foo2
        return x + this.Foo2;

        //Note that Foo *is* accessable here as 'SomeObject.Foo'
    }

}

Ich kann ehrlich sagen, dass ich mit Ausnahme der Erstellung von Erweiterungsmethoden ( Kurzanleitung zu Erweiterungsmethoden ) noch nie eine als statisch gekennzeichnete Klasse verwendet habe .

Wie auch immer, es gibt bestimmte Entwurfsmuster für die Verwendung statischer Methoden, wie Factory-Muster und Singleton-Muster . Wichtig ist jedoch, dass statische Methoden und Konstruktoren keine bestimmte Instanz einer Klasse verarbeiten (es sei denn, Sie übergeben eine). Normalerweise, um Berechnungen oder einen Vergleich zwischen Objekten durchzuführen. Die "Main" -Methode, auf die Sie sich beziehen, ist immer statisch. Weitere Informationen hierzu finden Sie in diesem Artikel .

Im Folgenden wird der Unterschied zwischen statischen und instanziierten Methoden, Feldern und Eigenschaften beschrieben.

public static void Main(string[] args)
{
    //This is a static method that starts a thread in an application
    // space.  At this point not everything actually has to be static...

    //Here is an instantiation with a parameterless contruction
    SomeObject obj = new SomeObject();

    //Here is an instantiation using a static factory method
    SomeObject obj2 = SomeObject.CreateSomeObject(3);

    //Getting field value from static field
    // Notice that this references the class name, not an instance
    int fooValue1 = SomeObject.Foo;

    //Getting property value from instance
    //  Note that this references an object instance
    int fooValue2 = obj2.Foo2;

    //Instance method must be called through an object
    obj2.AddSomething(4);  //if default constructor, would return 8

    //Static methods must be called through class name
    SomeObject.Add(4); //Returns 7
}

Schauen Sie sich auch diesen Beitrag an , um einen tieferen Einblick in statische Klassen zu erhalten.

iMortalitySX
quelle
18

Hier ist die Art und Weise, wie Joshua Bloch es erklärt, die ich wie die meisten seiner Aussagen als brillant empfinde (ja, ich bin ein Joshua Bloch-Fanjunge :)). Dies wird aus dem Gedächtnis zitiert.

Stellen Sie sich vor, eine Klasse ist das Äquivalent einer Blaupause für ein Haus. Stellen Sie sich dann vor, ein Haus wäre für die Blaupause, da eine Instanz der Klasse für die Klasse ist. Sie können eine Klasse (Blaupause) und mehrere Instanzen (Häuser) daraus erstellen lassen.

Der gesunde Menschenverstand schreibt nun vor, dass die meisten Funktionen / Verhaltensweisen, die ein Haus (eine Instanz) haben kann / kann, obwohl sie im Bauplan deklariert sind, erst verwendet werden können, wenn ein tatsächliches Haus (eine Instanz) aus diesem Blau besteht -print (Klasse). Wie, Ihre Blaupause könnte den Ort enthalten, an dem die Lichtschalter und die Glühbirnen gehen sollten, aber Sie haben keine Möglichkeit, diese Arbeit auf der Blaupause zu machen, müssen Sie das Haus tatsächlich bauen, um in der Lage zu sein Zum Ein- und Ausschalten des Lichtschalters und zum Ein- und Ausschalten bestimmter Glühbirnen.

Möglicherweise haben Sie jedoch ein Verhalten, das direkt auf die Blaupause anwendbar ist und auf das Sie direkt über die Blaupause zugreifen können, ohne ein tatsächliches Haus aus dieser Blaupause erstellen zu müssen. Stellen Sie sich vor, Ihre Blaupause hat eine Schaltfläche, die beim Drücken die Grundfläche des Hauses anzeigt, das in dieser Blaupause enthalten ist (indem Sie alle Längen der Wände und dergleichen berechnen). Natürlich KÖNNTEN Sie zuerst ein Haus bauen und dann seine Grundfläche messen, aber Sie können dies nur mit der Blaupause tun. Es wäre also hilfreicher, dieses Verhalten in der Blaupause zu implementieren. Solch eine eingebettete Blaupause-Schaltfläche, die die Grundfläche des Hauses berechnet, entspricht einer statischen Funktion in einer Klasse.

Shivan Drache
quelle
Oder Sie hätten eine BlueprintKlasse, die die Funktionalität der Blaupause implementiert, einschließlich der Fähigkeit, die durch die Blaupause ausgedrückte Grundfläche des Hauses zu berechnen. Diese Blueprint-Instanz wird dann an eine BuilderInstanz weitergeleitet, die wiederum die erforderlichen Aktionen Buildingausführt, um auf der Grundlage eines Blueprints eine potenziell beliebige Anzahl von Instanzen zu erstellen und auszugeben .
ein Lebenslauf am
11

So zu sehen hilft mir:

  • Jeder Typ hat eine statische Instanz.
  • Die statische Instanz wird beim ersten Zugriff auf den Typ erstellt - entweder über die statische Instanz oder durch das Erstellen einer anderen Instanz.
  • Sie können beliebig viele nicht statische Instanzen erstellen, es gibt jedoch nur eine statische Instanz.
  • Alles in einer Klasse, das als statisch deklariert ist, gehört zur statischen Instanz und hat daher keinen Zugriff auf andere von Ihnen erstellte Instanzen. Die anderen Instanzen haben jedoch Zugriff auf die statische Instanz.
  • Wenn eine Klasse als statisch deklariert ist, können Sie keine anderen Instanzen erstellen, sondern nur die statische Instanz.
  • Sie können einen statischen Konstruktor für die statische Instanz genauso wie einen Konstruktor für eine normale Instanz deklarieren (indem Sie ihn jedoch als statisch deklarieren).

Wann ist das Schlüsselwort static zu verwenden?

  • Jede Methode, die keinen Zugriff auf lokale Eigenschaften benötigt, kann und sollte als statisch deklariert werden.
  • Hilfsklassen, die überhaupt keinen Status haben (was sowieso selten sein sollte) und die niemals verspottet werden, können als statisch deklariert werden. Ob sie sollten, ist eine andere Sache; Verwenden Sie diese Funktion sparsam.
  • Eigenschaften und Felder, auf die alle Instanzen einer Klasse zugreifen müssen, müssen als statisch deklariert werden. Verwenden Sie dies jedoch nur, wenn es keine andere Option gibt.
pdr
quelle
+1, für eine gute Zusammenfassung, ich wusste nicht, dass jeder Typ 1 statische Instanz hat und ich finde es seltsam, Ihnen die Wahrheit zu sagen.
NoChance
2
@EmmadKareem Das ist nur ein mentales Modell, das pdr benutzt, weil "Looking at it this way helps"er. Du findest es seltsam, weil es nicht genau stimmt, aber du kannst es dir so vorstellen, wenn du willst. Kennen Sie das Bohr-Modell? Es präsentiert eine Reihe von Regeln und Ideen, wie Atome und Elektronen miteinander interagieren. Das Modell funktioniert je nachdem, was Sie tun, aber es ist nicht die Realität.
Phant0m
@phant0m, danke für die Erklärung, ich hatte den Eindruck, dass es sich um ein echtes Modell handelt und war deswegen überrascht.
NoChance
Tatsächlich gibt es manchmal Gründe, warum Sie eine Methode nicht erstellen möchten, staticobwohl keine lokalen Eigenschaften verwendet werden. Das Erstellen von Dingen statickann eine Kopplung zu Clients hinzufügen, da diese die Klasse direkt ansprechen müssen. Dies kann es beispielsweise schwieriger machen, einen Komponententest mit Spott durchzuführen.
Allan
@Allan: Wenn Sie eine öffentliche Methode für eine Klasse aufrufen, die sich nicht auf den Status einer Instanz dieser Klasse auswirkt, MUSS sie statisch sein, um dies dem Client-Entwickler klar zu machen. Wenn diese Methode so viel bewirkt, dass sie verspottet werden muss, ist dies ein anderes Problem, das auf verschiedene Arten gelöst werden kann.
pdr
3

Einfachste Erklärung --- Statisch => Pro Umgebung ist nur eine Kopie vorhanden.

In einer VM oder CLR gibt es also immer nur eine Kopie einer statischen Klasse, und jede andere Klasse, die auf sie verweist, muss ihre Methoden und Daten mit allen anderen Klassen teilen, die auf sie verweist.

Für eine statische Variable gibt es in der Laufzeitumgebung nur eine Instanz dieser Variablen, unabhängig davon, wie viele Kopien der besitzenden Klasse erstellt werden, wenn sie auf eine statische Variable verweisen. Sie verweisen alle auf denselben Speicherbereich.

James Anderson
quelle
1

Statische Mitglieder sind der Klasse zugeordnet, keiner Instanz dieser Klasse.

Da es sich um .NET handelt, sollten Sie die String- Klasse berücksichtigen , insbesondere die Split- und Join- Methoden.

Split ist eine Instanzmethode . Erstellen Sie eine String-Variable, geben Sie ihr einen Wert und Sie können Split () für diese Variable / diesen Wert aufrufen und ein Array von "Bits" zurückerhalten:

String s1 = "abc,def,ghi" ; 
String[] array2 = s1.Split( ',' ) ; 

So zum Beispiel Methoden hat der Wert innerhalb der gegebenen Klasseninstanz Angelegenheiten .

Join ist eine statische Methode. OK, es ergibt sich ein String Ergebnis , wenn ein Trennzeichen und ein String - Array gegeben zu kauen, so ist es „etwas zu tun mit“ der String - Klasse, aber es ist nicht mit einem bestimmten zugehörigen Wert in jedem String - Instanz ( in der Tat Instanz Werte für statische Methoden nicht verfügbar).
In anderen Sprachen war die Join-Methode möglicherweise auf die Array-Klasse (oder besser auf eine StringArray-Klasse) beschränkt, aber Our Friends in Redmond entschied, dass sie für die String-Klasse "relevanter" ist, sodass sie dort abgelegt wurde .

String[] array3 = { ... } 
s1 = String.Join( array3, "," ) ; 

Eine andere Alternative wäre eine Instanz- Join-Methode gewesen, bei der der in der Zeichenfolge [Klasseninstanz] enthaltene Wert als Join-Begrenzer verwendet wurde.

// Maybe one day ... 
String s4 = "," ; 
s1 = s4.Join( array3 ) ; 
Phill W.
quelle
1

Das staticSchlüsselwort kann für Neulinge etwas schwer zu fassen sein. Der primäre Zweck besteht darin, ein Klassenmitglied als nicht zu einer einzelnen Instanz der Klasse gehörend, sondern zur Klasse selbst zu identifizieren.

Ohne zu sehr ins Detail zu gehen, setzen C # (und Java) das objektorientierte Ideal, dass alle Codes und Daten zu einem Objekt gehören müssen, rigoros durch und sind daher in Umfang, Sichtbarkeit und Lebensdauer begrenzt. Dies ist im Allgemeinen die beste Vorgehensweise, wenn der grundlegende Grundsatz eines Objekts, das eine reale Sache darstellt, zutrifft. Dies ist jedoch nicht immer der Fall. Manchmal benötigen Sie eine Funktion oder Variable, auf die Sie von einer beliebigen Stelle im Code zugreifen können , ohne dass Sie einen Verweis auf ein Objekt weitergeben müssen, und mit der Garantie, dass die Daten, die Sie anzeigen oder ändern, genau das sind , was alle tun else befasst sich mit und nicht mit einer Kopie davon, die zu einer anderen Instanz eines Objekts gehört.

Ein solches Verhalten war in C und C ++ in Form der "globalen" Funktion oder Variable verfügbar, die nicht in ein Objekt eingekapselt war. Als Kompromiss unterstützen C # und Java den "statischen Bereich", einen Punkt zwischen wirklich globalem Code ohne übergeordnetes Objekt und Instanzmitgliedern mit eingeschränktem Bereich.

Jedes "Codemitglied" (Funktion, Eigenschaft, Feld), das als " staticGeltungsbereich" deklariert wurde, wird ab der ersten Zeile der Programmfunktion in den Geltungsbereich aufgenommen main()und verlässt das Programm erst, wenn die main()Funktion beendet wird. Im Klartext ist ein statischer Member vorhanden und kann verwendet werden, solange das Programm ausgeführt wird. Darüber hinaus werden statische Member aufgerufen, indem sie als Member des Typs selbst aufgerufen werden, nicht als Member einer Instanz dieses Typs:

public class Foo
{
   public int MyInt {get;set;} //this is an "instance member"
   public static int MyStaticInt {get;set;} //this is a "static member"
}

...

var myFoo = new Foo();
myFoo.MyInt = 5; //valid
myFoo.MyStaticInt = 5; //invalid; MyStaticInt doesn't belong to any one Foo

Foo.MyInt = 5; //invalid; MyInt only has meaning in the context of an instance
Foo.MyStaticInt = 2; //valid

Dadurch werden statische Member für jeden Code sichtbar, der über Kenntnisse des Typs verfügt, unabhängig davon, ob sie eine einzelne Instanz davon kennen oder nicht.

Um Ihre Frage zu beantworten, besteht der Hauptvorteil der Kennzeichnung als statisch darin, dass sie überall dort sichtbar wird, wo der Typ selbst bekannt ist, unabhängig davon, ob der konsumierende Code eine Instanz des enthaltenen Objekts hat oder erhalten kann. Es gibt auch einen leichten Leistungsvorteil; Da sich die Methode im statischen Bereich befindet, kann sie nur auf andere statische Member (derselben oder anderer Klassen) und auf alle als Parameter übergebenen Elemente zugreifen. Daher muss die Laufzeit keinen Verweis auf die aktuelle Instanz des enthaltenden Objekts auflösen, wie dies normalerweise für eine Instanzmethode der Fall wäre, um kontextspezifische Statusinformationen bereitzustellen.

Ganze Klassen können auch als statisch markiert werden. Auf diese Weise teilen Sie dem Compiler mit, dass die Klassendeklaration nur aus statischen Elementen besteht und daher nicht instanziiert werden kann. Auf diese Weise können Sie auf einfache Weise sicherstellen, dass sich nur eine einzige Kopie eines Objekts im Speicher befindet. mach die Klasse und alles darin statisch. Es ist jedoch sehr selten, dass dies die beste Lösung für einen solchen Bedarf ist. In einer Situation, in der genau eine Kopie eines Datensatzes erforderlich ist, wird stattdessen in der Regel der "Singleton" empfohlen. Dies ist eine nicht statische Klasse, die einen statischen Accessor und einen nicht öffentlichen Konstruktor verwendet, um den Zugriff auf eine einzelne Instanz von sich selbst zu ermöglichen. Theoretisch bietet ein Singleton die gleichen Vorteile wie eine vollständig statische Klasse, bietet jedoch die Möglichkeit, die Klasse instanzbasiert und objektorientiert zu verwenden.

KeithS
quelle