Zrozumienie statycznego słowa kluczowego


16

Mam doświadczenie w programowaniu w Javie, JavaScript i PHP.

Czytam Microsoft Visual C # 2010 krok po kroku, co moim zdaniem jest bardzo dobrą książką na temat zapoznania się z językiem C #.

Wydaje mi się, że mam problemy ze zrozumieniem statycznego słowa kluczowego. Z tego, co rozumiem do tej pory, jeśli klasa jest deklarowana jako statyczna, wszystkie metody i zmienne muszą być statyczne. Metoda główna zawsze jest metodą statyczną, więc w klasie, która istnieje metoda główna, wszystkie zmienne i metody są deklarowane jako statyczne, jeśli trzeba je wywołać w metodzie głównej. Zauważyłem również, że aby wywołać metodę statyczną z innej klasy, nie trzeba tworzyć obiektu, którego można użyć nazwy klasy.

Ale jaki jest rzeczywisty cel statycznego słowa kluczowego? Kiedy powinienem zadeklarować zmienną statyczną i metody?


4
statyczny w C # jest prawie taki sam jak statyczny w Javie. Jeśli rozumiesz to w Javie, nie możesz mieć żadnych problemów w C #
superM

Java była moim pierwszym językiem programowania i tam też nie rozumiałem tej koncepcji. Używałem Javy tylko przez krótki czas
Nistor Alexandru

W skrócie: używaj „statycznego”, gdy nie potrzebujesz orientacji obiektowej, na przykład tylko niektóre samodzielne metody lub zmienne. Uznanie, że klasa jest statyczna, oznacza umieszczenie tych nieobiektywnych funkcji i zmiennych tylko we wspólnej nazwie (spacji), nazwie klasy.
Doc Brown,

Odpowiedzi:


15

Słowo „statyczne” w języku C # odnosi się do czegoś w klasie lub do samej klasy, które jest wspólne dla wszystkich instancji klasy. Na przykład, do pola oznaczonego jako statyczne można uzyskać dostęp ze wszystkich instancji tej klasy poprzez nazwę klasy.

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

}

Mogę szczerze powiedzieć, że nigdy nie korzystałem z klasy oznaczonej jako statyczna, z wyjątkiem tworzenia metod rozszerzania ( Szybki samouczek na temat metod rozszerzania ).

W każdym razie istnieją specyficzne wzorce projektowe do wykorzystania metod statycznych, takie jak wzorzec fabryczny i wzorzec singletonu , ale ważne jest, aby pamiętać, że metody statyczne i konstruktory nie zajmują się żadnym konkretnym wystąpieniem klasy (chyba że podasz jeden) zwykle w celu wykonania obliczeń lub porównania między obiektami. Metoda „główna”, do której się odwołujesz, jest zawsze statyczna, ale aby zobaczyć ją z innego punktu widzenia, zobacz ten artykuł .

Aby to kontynuować, oto jak nazywa się różnicę między metodami statycznymi i instancjami, polami i właściwościami.

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
}

Sprawdź także ten post , aby uzyskać głębsze spojrzenie na klasy statyczne.


18

Oto sposób na wyjaśnienie tego przez Joshua Blocha, który wydaje mi się genialny jak większość jego wypowiedzi (tak, jestem fanem Joshua Blocha :)). To jest cytowane z pamięci.

Wyobraź sobie, że klasa jest odpowiednikiem niebieskiego druku dla domu. Wyobraź sobie, że dom jest na niebiesko, jako instancja klasy dla klasy. Można z niej utworzyć jedną klasę (wydruk niebieski) i wiele instancji (domów).

Teraz zdrowy rozsądek mówi, że większość funkcji / zachowań, które dom (instancja) może mieć / wykonuje, nawet jeśli są zadeklarowane na niebiesko, nie można użyć, dopóki rzeczywisty dom (instancja) nie zostanie wykonany z tego niebieskiego -print (klasa). Na przykład twój niebieski wydruk może zawierać w nim miejsce, w którym powinny iść światła i żarówki, ale nie masz możliwości, aby działały na niebieskim druku, musisz faktycznie zbudować dom, aby móc włączanie i wyłączanie włącznika światła oraz włączanie i wyłączanie niektórych żarówek.

Jednak możesz mieć pewne zachowanie, które dotyczy bezpośrednio niebieskiego druku, i którego możesz użyć / uzyskać dostęp bezpośrednio na niebieskim wydruku, bez potrzeby tworzenia prawdziwego domu z tego niebieskiego wydruku. Wyobraź sobie, że twój niebieski nadruk ma przycisk, który po naciśnięciu wyświetli ślad domu zawarty w tym niebieskim nadruku (obliczając wszystkie długości ścian i tym podobne). Oczywiście MUSISZ najpierw zbudować dom, a potem odmierzyć jego powierzchnię, ale możesz to zrobić z samym niebieskim drukiem, więc bardziej pomocne byłoby wdrożenie tego zachowania w niebieskim druku. Taki osadzony na niebiesko przycisk, który oblicza powierzchnię domu, jest równoważny z funkcją statyczną w klasie.


Albo miałbyś Blueprintklasę, która implementuje funkcjonalność planu, w tym zdolność do obliczania powierzchni domu wyrażonej przez plan. Ta instancja schematu jest następnie przekazywana do Builder(z kolei prawdopodobnie samej instancji), który z kolei robi to, co jest potrzebne do skonstruowania i wygenerowania potencjalnie dowolnej liczby Buildinginstancji w oparciu o plan.
CVn

12

Patrzenie na to w ten sposób pomaga mi:

  • Każdy typ ma instancję statyczną.
  • Instancja statyczna jest tworzona przy pierwszym dostępie do typu - za pośrednictwem instancji statycznej lub tworzenia innej instancji.
  • Możesz utworzyć dowolną liczbę niestatycznych instancji, ale istnieje tylko jedna instancja statyczna.
  • Wszystko w klasie, która jest zadeklarowana jako statyczna, należy do instancji statycznej, a zatem nie ma dostępu do żadnych innych tworzonych instancji. Ale inne instancje mają dostęp do instancji statycznej.
  • Jeśli klasa zostanie zadeklarowana jako statyczna, nie będzie można tworzyć innych instancji, tylko instancja statyczna może istnieć.
  • Możesz zadeklarować konstruktora statycznego dla instancji statycznej, tak jak konstruktor dla instancji normalnej (ale deklarując ją jako statyczną).

Co do tego, kiedy użyć statycznego słowa kluczowego:

  • Każda metoda, która nie potrzebuje dostępu do lokalnych właściwości, może i prawdopodobnie powinna zostać uznana za statyczną.
  • Klasy pomocnicze, które nie mają żadnego stanu (który i tak powinien być rzadki) i które nigdy nie będą wyśmiewane, można uznać za statyczne. Czy powinny, to inna sprawa; korzystaj z tej funkcji oszczędnie.
  • Właściwości i pola, do których muszą mieć dostęp wszystkie instancje klasy, muszą być zadeklarowane jako statyczne. Ale używaj tego tylko wtedy, gdy nie ma innej opcji.

+1, dla dobrego podsumowania, nie wiedziałem, że każdy typ ma 1 instancję statyczną i dziwne jest mówienie prawdy.
NoChance

2
@EmmadKareem To tylko model mentalny, którego używa pdr, ponieważ "Looking at it this way helps"on. Uważasz to za dziwne, ponieważ nie jest to do końca prawda, ale możesz tak o tym myśleć, jeśli chcesz. Czy znasz model Bohr? Jest to zestaw reguł i pomysłów na to, jak atomy i elektrony oddziałują na siebie. Modelu pracuje w zależności od tego, co robisz, ale to nie jest rzeczywistość.
phant0m

@ phant0m, dzięki za wyjaśnienie, miałem wrażenie, że to nie był prawdziwy model i byłem z tego powodu zaskoczony.
NoChance

W rzeczywistości istnieją czasem powody, dla których możesz nie chcieć tworzyć metody, staticnawet jeśli nie używa właściwości lokalnych. Tworzenie rzeczy staticmoże dodać sprzężenie do klientów, ponieważ muszą oni bezpośrednio zwracać się do klasy. Na przykład może to utrudnić testowanie jednostkowe z kpiną.
Allan

@Allan: Prawdopodobnie, jeśli wywołujesz metodę publiczną w klasie, która nie wpływa na stan instancji tej klasy, POWINNA być statyczna, aby było to jasne dla programisty klienta. Jeśli ta metoda robi tyle, że wymaga kpiny, jest to inny problem, który można rozwiązać na wiele różnych sposobów.
pdr

4

Najprostsze wyjaśnienie --- Statyczne => Dla jednego środowiska będzie istnieć tylko jedna kopia.

Tak więc w maszynie wirtualnej lub CLR będzie zawsze tylko jedna kopia klasy statycznej i każda inna klasa, do której się ona odwołuje, będzie musiała dzielić swoje metody i dane ze wszystkimi innymi klasami, które się do niej odwołują.

W przypadku zmiennej statycznej będzie istniała tylko jedna instancja tej zmiennej w środowisku wykonawczym, bez względu na to, ile kopii klasy właściciela jest utworzonych, gdy odwołują się do zmiennej statycznej, wszystkie będą odnosić się do tego samego elementu pamięci.


2

Członkowie statyczni są powiązani z klasą, a nie z żadną instancją tej klasy.

Ponieważ mówimy o .Net, rozważ klasę String , w szczególności metody Split i Join .

Split jest metodą instancji . Utwórz zmienną String, nadaj jej wartość, a następnie możesz wywołać Split () dla tej zmiennej / wartości i uzyskać tablicę „bitów”:

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

Tak więc, na przykład metody, wartość przechowywana w danej instancji klasy ma znaczenie .

Łączenie jest metodą statyczną . OK, to daje ciąg wynik podawany do separatora i tablicę ciągów do gryzienia, więc jest to „coś zrobić z” klasy String, ale to nie wiąże się z żadnym konkretnym wartości w dowolnej instancji String (rzeczywiście, wartości instancji są niedostępne dla metod statycznych).
W innych językach metoda Join mogła zostać „przyczepiona” do klasy Array (lub, być może lepiej, klasy StringArray), ale nasi przyjaciele z Redmond zdecydowali, że jest bardziej „odpowiednia” dla klasy String, więc umieścili ją tam .

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

Inną alternatywą może być zastosowanie metody Join instancji , w której wartość przechowywana w String [instancja klasy] służyła nam jako ogranicznik łączenia, coś w stylu:

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

2

Słowo statickluczowe może być trochę trudne do zrozumienia dla początkujących. Jego podstawowym celem jest identyfikacja członka klasy, który nie należy do żadnej pojedynczej instancji klasy, ale zamiast do samej klasy.

Bez wchodzenia w zbyt wiele szczegółów, C # (i Java) sztywno egzekwują zorientowany obiektowo ideał, że cały kod i dane muszą należeć do obiektu, a zatem mają ograniczony zakres, widoczność i żywotność. Jest to ogólnie najlepsza praktyka wszędzie tam, gdzie ma zastosowanie podstawowa zasada obiektu reprezentującego jakąś rzeczywistość. Jednak nie zawsze; czasami potrzebna jest funkcja lub zmienna, do której można uzyskać dostęp z dowolnego miejsca w kodzie, bez konieczności przekazywania odniesienia do obiektu zawierającego ją, a także z gwarancją, że dane, które oglądasz lub zmieniasz, są dokładnie tym , co wszyscy else ma do czynienia, a nie jego kopię należącą do innej instancji obiektu.

Takie zachowanie było dostępne w C i C ++ w postaci funkcji lub zmiennej „globalnej”, która nie została zamknięta w obiekcie. Tak więc, jako kompromis, C # i Java obsługują „zakres statyczny”, półmetka między prawdziwie globalnym kodem bez obiektu nadrzędnego i elementami instancji o ograniczonym zakresie.

Każdy „element kodu” (funkcja, właściwość, pole) zadeklarowany jako staticwchodzi w zakres od pierwszego wiersza funkcji programu main()i nie opuszcza go, dopóki main()funkcja się nie zakończy. W zwykłym języku angielskim istnieje element statyczny i można go używać, dopóki program jest uruchomiony. Ponadto elementy statyczne są wywoływane przez wywoływanie ich jako członków samego typu, a nie członków jednej instancji tego typu:

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

Dzięki temu elementy statyczne są widoczne dla każdego kodu, który ma wiedzę o typie, niezależnie od tego, czy wiedzą o jakimkolwiek pojedynczym wystąpieniu tego kodu.

Aby odpowiedzieć na twoje pytanie, podstawową zaletą oznaczenia czegoś jako statycznego jest to, że staje się on widoczny wszędzie tam, gdzie znany jest sam typ, niezależnie od tego, czy konsumujący kod ma lub może uzyskać instancję zawierającego obiekt. Istnieje również niewielka poprawa wydajności; ponieważ metoda ma zakres statyczny, może uzyskiwać dostęp tylko do innych elementów statycznych (tej samej klasy lub innych) i do wszystkiego, co jest przekazywane jako parametr. Dlatego środowisko wykonawcze nie musi rozpoznawać żadnego odwołania do bieżącej instancji obiektu zawierającego, tak jak zwykle w przypadku metody instancji w celu zapewnienia informacji o stanie specyficznych dla kontekstu.

Całe klasy można również oznaczyć statycznie; robiąc to, informujesz kompilator, że deklaracja klasy będzie składać się wyłącznie z elementów statycznych, a zatem nie może być utworzona. Jest to łatwy sposób na upewnienie się, że w pamięci jest jedna i tylko jedna kopia obiektu; uczyń klasę i wszystko w niej statycznym. Jednak bardzo rzadko jest to najlepsze rozwiązanie takiej potrzeby. W sytuacji, gdy wymagana jest dokładnie jedna kopia zestawu danych, zwykle zaleca się stosowanie „singletonu”; jest to klasa niestatyczna, która używa statycznego akcesora i niepublicznego konstruktora, aby zapewnić dostęp do pojedynczej instancji samego siebie. Teoretycznie singleton zapewnia te same korzyści, co klasa w pełni statyczna, ale z dodatkową możliwością korzystania z klasy w sposób obiektowy i obiektowy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.