Comprensione della parola chiave statica


15

Ho una certa esperienza nello sviluppo con Java, Javascript e PHP.

Sto leggendo Step by Step di Microsoft Visual C # 2010 che ritengo sia un ottimo libro per introdurti al linguaggio C #.

Mi sembra di avere problemi nella comprensione della parola chiave statica. Da quello che ho capito fino a questo punto se una classe è dichiarata statica, tutti i metodi e le variabili devono essere statici. Il metodo principale è sempre un metodo statico, pertanto nella classe in cui esiste il metodo principale tutte le variabili e i metodi vengono dichiarati statici se è necessario chiamarli nel metodo principale. Inoltre ho notato che per chiamare un metodo statico da un'altra classe non è necessario creare un oggetto che è possibile utilizzare il nome della classe.

Ma qual è lo scopo effettivo della parola chiave statica? Quando devo dichiarare variabili e metodi statici?


4
static in C # è quasi uguale a static in Java. Se lo capisci in Java, non devi avere problemi in C #
superM

Java è stato il mio primo linguaggio di programmazione e non ho capito neanche questo concetto. Ho usato Java solo per un breve periodo di tempo
Nistor Alexandru,

In breve: utilizzare "statico" quando non è necessario l'orientamento agli oggetti, ad esempio solo alcuni metodi o variabili indipendenti. Dichiarare una classe come statica significa mettere quelle funzioni non orientate agli oggetti e variabili solo in un nome comune (spazio), il nome della classe.
Doc Brown,

Risposte:


14

La parola chiave "statica" in C # si riferisce a qualcosa nella classe, o alla classe stessa, che è condivisa tra tutte le istanze della classe. Ad esempio, è possibile accedere a un campo contrassegnato come statico da tutte le istanze di quella classe tramite il nome della classe.

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'
    }

}

Posso onestamente dire che non ho mai usato una classe contrassegnata come statica ad eccezione della creazione di metodi di estensione ( Tutorial rapido sui metodi di estensione ).

Ad ogni modo, ci sono modelli di progettazione specifici per l'utilizzo di metodi statici, come il modello factory e il modello singleton , ma la cosa importante da ricordare è che i metodi statici e i costruttori non gestiscono alcuna istanza specifica di una classe (a meno che non ne passi uno), tipicamente per fare calcoli o fare un confronto tra oggetti. Il metodo "Principale" a cui ti riferisci è sempre statico, ma per vederlo da un altro punto di vista, consulta questo articolo .

Per dare seguito a questo, ecco come viene chiamata la differenza tra metodi, campi e proprietà statici e istanziati.

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
}

Inoltre, controlla questo post per uno sguardo più approfondito alle classi statiche.


18

Ecco il modo di spiegarlo di Joshua Bloch, che trovo geniale come la maggior parte di ciò che dice (sì, sono un fan boy di Joshua Bloch :)). Questo è citato dalla memoria.

Immagina che una classe sia l'equivalente di una stampa blu per una casa. Immagina quindi che una casa sia stampata come un'istanza della classe è per la classe. Puoi avere una classe (blue-print) e più istanze (case) create da essa.

Ora, il buon senso impone che la maggior parte delle funzioni / comportamenti che una casa (istanza) può avere / fare, anche se sono dichiarati nella stampa blu, non può essere usata fino a quando una casa reale (istanza) non è fatta da quel blu -print (classe). Ad esempio, la tua stampa blu potrebbe contenere al suo interno il punto in cui dovrebbero andare gli interruttori della luce e le lampadine, ma non hai modo di far funzionare quelli sulla stampa blu, devi effettivamente costruire la casa per poter per accendere e spegnere la luce e far accendere e spegnere alcune lampadine.

Tuttavia, potresti avere un comportamento applicabile direttamente alla stampa blu e che potresti utilizzare / accedere direttamente alla stampa blu senza la necessità di ricavare una casa reale da quella stampa blu. Immagina che la tua stampa blu abbia un pulsante che, premendo, mostrerà l'impronta della casa contenuta in quella stampa blu (calcolando tutte le lunghezze delle pareti e così via). Ovviamente POTREBBE costruire prima una casa, quindi andare in giro a misurare la sua impronta, ma puoi farlo solo con la sua blue-print, quindi sarebbe più utile avere questo comportamento implementato nella blue-print. Un tale pulsante incorporato blu-stampato che calcola l'impronta della casa è l'equivalente di avere una funzione statica in una classe.


Oppure avresti una Blueprintclasse che implementa la funzionalità del progetto, inclusa la capacità di calcolare l'impronta della casa espressa dal progetto. Questa istanza del modello viene quindi inviata a un Builder(a sua volta probabilmente un'istanza), che a sua volta fa ciò che è necessario per costruire e produrre un numero potenzialmente arbitrario di Buildingistanze basato su un modello.
un CVn

11

Guardarlo in questo modo mi aiuta a:

  • Ogni tipo ha un'istanza statica.
  • L'istanza statica viene creata al primo accesso al tipo, tramite l'istanza statica o creando un'altra istanza.
  • Puoi creare tutte le istanze non statiche che desideri, ma esiste solo un'istanza statica.
  • Qualsiasi cosa all'interno di una classe dichiarata come statica appartiene all'istanza statica e quindi non ha accesso ad altre istanze create. Ma le altre istanze hanno accesso all'istanza statica.
  • Se una classe viene dichiarata come statica, non è possibile creare altre istanze, può esistere solo l'istanza statica.
  • Puoi dichiarare un costruttore statico per l'istanza statica proprio come un costruttore per un'istanza normale (ma dichiarandolo statico).

Per quanto riguarda quando utilizzare la parola chiave statica:

  • Qualsiasi metodo che non necessita dell'accesso alle proprietà locali può e probabilmente deve essere dichiarato statico.
  • Le classi di aiuto che non hanno alcuno stato (che dovrebbe essere comunque raro) e che non saranno mai derise possono essere dichiarate statiche. Se dovrebbero essere un'altra questione; usa questa funzionalità con parsimonia.
  • Le proprietà e i campi a cui devono accedere tutte le istanze di una classe devono essere dichiarati statici. Ma usa questo solo quando non c'è altra opzione.

+1, per un buon riassunto, non sapevo che ogni tipo abbia 1 istanza statica e trovo strano dirti la verità.
NoChance,

2
@EmmadKareem Questo è solo un modello mentale che pdr usa, perché "Looking at it this way helps"lui. Lo trovi strano perché non è esattamente vero, ma puoi pensarlo così se vuoi. Conosci il modello Bohr? È un insieme di regole e idee su come gli atomi e gli elettroni interagiscono tra loro. Il modello funziona in base a ciò che fai, ma non è la realtà.
phant0m

@ phant0m, grazie per la spiegazione, avevo l'impressione che non fosse un modello reale e sono rimasto sorpreso per questo.
NoChance,

In realtà, a volte ci sono motivi per cui potresti non voler creare un metodo staticanche se non usa le proprietà locali. Fare cose staticpuò aggiungere l'accoppiamento ai client perché devono rivolgersi direttamente alla classe. Ad esempio, ciò può rendere più difficile il test unitario con derisione.
Allan,

@Allan: Probabilmente, se stai chiamando un metodo pubblico su una classe che non influenza lo stato di un'istanza di quella classe, DOVREBBE essere statico, per chiarirlo allo sviluppatore del client. Se quel metodo fa così tanto che ha bisogno di essere deriso, è un problema diverso che può essere risolto in diversi modi.
pdr

3

Spiegazione più semplice --- Statico => Esiste una sola copia per ambiente.

Quindi all'interno di una VM o CLR ci sarà sempre una sola copia di una classe statica, e qualsiasi altra classe che fa riferimento dovrà condividere i suoi metodi e dati con tutte le altre classi che fanno riferimento a essa.

Per una variabile statica, ci sarà solo un'istanza di questa variabile nell'ambiente di runtime, indipendentemente da quante copie della classe proprietaria vengono create quando fanno riferimento a una variabile statica, faranno tutti riferimento allo stesso spazio di archiviazione.


1

I membri statici sono associati alla Classe, non a nessuna istanza di quella Classe.

Dato che stiamo parlando di .Net, considera la classe String , in particolare i metodi Split e Join .

La divisione è un metodo di istanza . Crea una variabile String, assegnagli un valore e puoi chiamare Split () su quella variabile / valore e recuperare una matrice di "bit":

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

Pertanto, ad esempio i metodi, il valore contenuto nell'istanza di classe indicata è importante .

Unire è un metodo statico . OK, produce un risultato String quando viene dato un delimitatore e un array String da masticare, quindi è "qualcosa a che fare con" la Classe String, ma non è associato a nessun valore particolare in nessuna istanza String (in effetti, i valori di istanza sono non disponibile per i metodi statici).
In altre lingue, il metodo Join potrebbe essere stato "bloccato" nella classe Array (o forse meglio, una classe StringArray) ma i nostri amici di Redmond hanno deciso che era più "pertinente" per la classe String, quindi l'hanno messa lì .

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

Un'altra alternativa potrebbe essere stata quella di avere un metodo Join di istanza , in cui il valore contenuto nella stringa [istanza di classe] usasse come delimitatore di join, qualcosa del tipo:

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

1

La staticparola chiave può essere un po 'difficile da comprendere per i neofiti. Il suo scopo principale è quello di identificare un membro della classe come non appartenente a nessuna singola istanza della classe, ma invece alla classe stessa.

Senza entrare troppo nei dettagli, C # (e Java) impongono rigidamente l'ideale orientato agli oggetti che tutto il codice e i dati devono appartenere a un oggetto, e quindi sono limitati in ambito, visibilità e durata. Questa è generalmente la migliore pratica ovunque si applichi il principio fondamentale di un oggetto che rappresenta qualcosa del mondo reale. Tuttavia, non sempre; a volte ciò di cui hai bisogno è una funzione o una variabile a cui puoi accedere da qualsiasi parte del codice, senza la necessità di passare un riferimento a un oggetto che lo contiene e con la garanzia che i dati che stai osservando o modificando sono esattamente ciò che tutti else sta trattando, e non una sua copia appartenente a un'istanza diversa di un oggetto.

Tale comportamento era disponibile in C e C ++ nella forma della funzione o variabile "globale", che non era incapsulata in un oggetto. Quindi, come compromesso, C # e Java supportano "ambito statico", un punto a metà strada tra codice veramente globale senza oggetto padre e membri di istanza di ambito limitato.

Qualsiasi "membro del codice" (funzione, proprietà, campo) dichiarato staticrientrante nell'ambito della prima riga della main()funzione del programma e non lo lascia fino al main()termine della funzione. In parole povere, esiste un membro statico che può essere utilizzato finché il programma è in esecuzione. Inoltre, i membri statici vengono richiamati chiamandoli come membri del tipo stesso, non membri di una sola istanza di quel tipo:

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

Ciò rende i membri statici visibili a qualsiasi codice che abbia conoscenza del tipo, indipendentemente dal fatto che ne siano a conoscenza o meno.

Per rispondere alla tua domanda, il vantaggio principale di contrassegnare qualcosa come statico è che diventa visibile ovunque sia noto il tipo stesso, indipendentemente dal fatto che il codice utilizzatore abbia o possa ottenere un'istanza dell'oggetto contenente. C'è anche un leggero vantaggio in termini di prestazioni; poiché il metodo ha un ambito statico, può accedere solo ad altri membri statici (della stessa classe o di altri) e a qualunque cosa venga passata come parametro. Pertanto, il runtime non deve risolvere alcun riferimento all'istanza corrente dell'oggetto contenitore, come dovrebbe normalmente fare per un metodo di istanza al fine di fornire informazioni sullo stato specifiche del contesto.

Intere classi possono anche essere contrassegnate come statiche; in tal modo, si comunica al compilatore che la dichiarazione di classe sarà composta esclusivamente da membri statici e quindi non può essere istanziata. Questo è un modo semplice per assicurarsi che ci sia una, e una sola, copia di un oggetto in memoria; rendere la classe e tutto quanto in essa statica. Tuttavia, è molto raro che questa sia la migliore soluzione a tale esigenza. In una situazione in cui è richiesta esattamente una copia di una serie di dati, si consiglia invece il "singleton"; questa è una classe non statica, che utilizza un accessor statico e un costruttore non pubblico per fornire l'accesso a una singola istanza di se stessa. Teoricamente, un singleton offre più o meno gli stessi vantaggi di una classe completamente statica, ma con la possibilità aggiuntiva di usare la classe in un modo basato sull'istanza e orientato agli oggetti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.