Czytanie statyczne vs const

1386

Czytałem o consti static readonlypola. Mamy pewne klasy, które zawierają tylko wartości stałe. Używane do różnych rzeczy w naszym systemie. Zastanawiam się więc, czy moja obserwacja jest poprawna:

Czy tego rodzaju stałe wartości powinny zawsze dotyczyć static readonlywszystkiego, co publiczne? I używać tylko constdo wewnętrznych / chronionych / prywatnych wartości?

Co polecasz? Czy może nie powinienem nawet używać static readonlypól, a raczej używać właściwości?

Svish
źródło
5
Oto bardzo interesujący pojedynczy przypadek, który właśnie znalazłem na korzyść static readonly: spróbuj użyć stałej w środku, IEnumeratorktóra wyzwalałaby niemożliwy do sprawdzenia,yield a dostaniesz przerażający „Wewnętrzny błąd kompilatora” . Nie testowałem kodu poza Unity3D, ale ufam, że jest to błąd mono lub .NET . Jest to jednak problem c # .
cregox
8
inną różnicą jest to, że można użyć stałego ciągu w przełączniku, ale nie statycznego ciągu tylko do odczytu
flagg19 24.04.15
7
static readonlynie może być użyty w switch-caseinstrukcji jako casezmienna, constjest wymagany do tego celu.
Mostafiz Rahman
3
static readonlynie może być również użyty jako parametr atrybutu
Dread Boy

Odpowiedzi:

940

public static readonlypola są trochę niezwykłe; public staticwłaściwości (tylko z a get) byłyby bardziej powszechne (być może poparte private static readonlypolem).

constwartości są zapisywane bezpośrednio w witrynie wywołującej; to jest obosieczne:

  • nie ma sensu, jeśli wartość jest pobierana w czasie wykonywania, być może z config
  • jeśli zmienisz wartość stałej, musisz odbudować wszystkich klientów
  • ale może być szybszy, ponieważ pozwala uniknąć wywołania metody ...
  • ... które i tak czasami mogły zostać wprowadzone przez JIT

Jeśli wartość nigdy się nie zmieni, to const jest w porządku - Zeroetc tworzy rozsądne const; p Poza tym staticwłaściwości są bardziej powszechne.

Marc Gravell
źródło
13
Dlaczego nieruchomość nad polem? Jeśli jest to niezmienna klasa, nie widzę różnicy.
Michael Hedgpeth
73
@Michael - te same powody jak zawsze; ukrywa wdrożenie. Może się okazać (później), że musisz być leniwie załadowany, oparty na konfiguracji, elewacji lub cokolwiek innego. W rzeczywistości albo często byłoby dobrze ...
Marc Gravell
42
@ CoffeeAddict z definicji, stała nie pobiera wartości z pliku konfiguracyjnego; w czasie kompilacji jest wypalany dosłownie Jedynym sposobem na użycie stałej w czasie wykonywania jest refleksja nad polami. Za każdym razem, gdy spróbujesz go użyć, kompilator już zastąpił twoje stałe użycie dosłowne ; tzn. jeśli metoda w twoim kodzie używa 6 stałych i sprawdzasz ją jako IL, nie będzie wzmianki o żadnych ciągłych przeglądach; dosłowne wartości zostaną po prostu załadowane na miejscu
Marc Gravell
37
@MarcGravell - UWAGA: readonlypola nie mogą być używane w instrukcjach switch / case, zamiast tego trzeba const.
Luciano
7
@didibus Zmiana pola na właściwość w rzeczywistości psuje interfejs API. Pole w języku C # skutecznie działa jak zmienna, podczas gdy właściwość w języku C # jest pomocnikiem składni do pisania metody gettera i / lub metody settera. Ta różnica jest ważna, gdy w grę wchodzą inne zespoły. Jeśli zmienisz pole na właściwość, a inne zestawy zależą od tego pola, wówczas te inne zestawy muszą zostać ponownie skompilowane.
Stephen Booher
237

Użyłbym, static readonlyjeśli konsument jest w innym zespole. Posiadanie Konsumentaconst i Konsumenta w dwóch różnych zespołach to dobry sposób na zastrzelenie się w stopę .

Michael Stum
źródło
5
Tak więc myślę, że jak niektórzy wspominali lub do których się nawiązywali, rozsądne może być użycie const dla wartości, które są tak naprawdę dobrze znanymi stałymi, jeśli zostaną upublicznione, w przeciwnym razie powinny być zastrzeżone dla zakresu dostępu wewnętrznego, chronionego lub prywatnego.
jpierson
1
@Dio Powodem, dla którego wciąż istnieje, jest to, że nie jest to problem sam w sobie - należy o tym pamiętać, ale zdolność do wstawiania stałych między granicami zespołu jest dobra dla wydajności. To naprawdę kwestia zrozumienia, że ​​„stały” oznacza „nigdy się nie zmieni”.
Michael Stum
1
@MichaelStum Ok Nie powinienem nazywać tego „problemem”. W mojej pracy mam const i dzielę się nim między zespołami, ale kompiluję ponownie dla każdego wdrożenia lub wysyłki kodu. Niemniej jednak fakt ten zdecydowanie warto wziąć pod uwagę.
Dio Phung
1
Tak więc ogólnie internal constlub w public static readonlyzależności od pożądanej widoczności.
Iiridayn
2
@Iiridayn Tak, to nie jest zły sposób patrzenia na to. Należy rozważyć kilka przypadków na krawędziach (np. Jeśli używa się Reflection lub jeśli wartość jest potrzebna dla atrybutu), i istnieją prawidłowe zastosowania dla public const(np. Jakiejkolwiek części standardu. Za każdym razem, gdy pracuję z XML, istnieje plik przestrzeni nazw z kilkoma public const string.) Ale ogólnie public constnależy go używać tylko po odpowiednim rozważeniu konsekwencji.
Michael Stum
199

Należy zwrócić uwagę na kilka bardziej istotnych kwestii:

const int a

  • musi zostać zainicjowany.
  • inicjalizacja musi nastąpić w czasie kompilacji .

readonly int a

  • może użyć wartości domyślnej bez inicjowania.
  • inicjalizacji można dokonać w czasie wykonywania (edycja: tylko w obrębie konstruktora).
Piotr
źródło
39
w ramach ctorjedynego.
Amit Kumar Ghosh
1
Nie tylko w konstruktorze, ale także w deklaracji ( docs.microsoft.com/en-us/dotnet/csharp/language-reference/… ).
deChristo
176

To tylko uzupełnienie innych odpowiedzi. Nie powtórzę ich (teraz cztery lata później).

Są sytuacje, w których a consti non-const mają inną semantykę. Na przykład:

const int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

drukuje True, natomiast:

static readonly int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

pisze False.

Powodem jest to, że metoda x.Equalsma dwa przeciążenia, jeden przyjmujący short( System.Int16) i drugi przyjmujący object( System.Object). Teraz pytanie brzmi, czy jedno lub oba dotyczą mojego yargumentu.

Gdy yjest stałą czasową kompilacji (dosłowną), w tym constprzypadku staje się ważne, że istnieje niejawna konwersja z int na short pod warunkiem, że intjest ona stała, i pod warunkiem, że kompilator C # zweryfikuje, czy jego wartość mieści się w zakresie short( który 42jest). Zobacz Implikowane konwersje wyrażeń stałych w specyfikacji języka C #. Dlatego należy wziąć pod uwagę oba przeciążenia. Przeciążenie Equals(short)jest preferowane (każdy shortjest object, ale nie wszystkie objectshort). Tak yjest konwertowane na shorti używane jest przeciążenie. Następnie Equalsporównuje dwa shorto identycznej wartości, a to daje true.

Gdy ynie jest stałą, nie istnieje niejawna konwersja z intna short. To dlatego, że ogólnie rzecz biorąc intmoże być zbyt duże, aby zmieściło się w short. (Istnieje wyraźna konwersja, ale nie powiedziałem Equals((short)y), więc to nie ma znaczenia.) Widzimy, że ma zastosowanie tylko jedno przeciążenie, Equals(object)jedno. Więc yjest zapakowane w object. Następnie Equalsporównamy a System.Int16do a System.Int32, a ponieważ typy wykonawcze nawet się nie zgadzają, to da false.

Dochodzimy do wniosku, że w niektórych (rzadkich) przypadkach zmiana elementu consttypu na static readonlypole (lub w inny sposób, gdy jest to możliwe) może zmienić zachowanie programu.

Jeppe Stig Nielsen
źródło
17
Dobry dodatek do zaakceptowanej odpowiedzi. Chciałbym dodać, że odpowiednia konwersja typów danych i inne podobne wytyczne (takie jak try catch) itp. Powinny być podstawą doświadczonych programistów, a nie kompilatorem. Niemniej jednak nauczyłem się stąd czegoś nowego. Dziękuję Ci.
Uknight
Wow, programuję w języku C # od dłuższego czasu i nigdy bym nie zgadł, że stała w zakresie krótkiego może być domyślnie zamieniona na krótką. Muszę powiedzieć, że to dość dziwne. Uwielbiam język C #, ale te dziwne niespójności, które wydają się nie dodawać dużej wartości, ale dodają dużo wymaganej mocy mózgu, aby stale rozważać, mogą być denerwujące, szczególnie dla początkujących.
Mike Marynowski,
@MikeMarynowski To prawda. Myślę jednak, że wprowadzili tę zasadę (między innymi), aby oświadczenie było short x = 42;legalne. Ponieważ masz int, mianowicie dosłowny 42, który domyślnie zamienia się w short x. Ale mogliby ograniczyć to tylko do literałów liczbowych; zdecydowali się jednak zezwolić na takie rzeczy, jak short x = y;gdzie yjest zdefiniowany jako const int y = 42;, a potem skończyli z tym.
Jeppe Stig Nielsen
87

Należy zauważyć, że const jest ograniczony do typów pierwotnych / wartości (wyjątek stanowią ciągi znaków)

Chris S.
źródło
30
Właściwie constmożna go używać także do innych typów, z tym wyjątkiem, że należy go zainicjować na wartość null, co czyni go bezużytecznym :)
nawfal
6
wyjątek jak w System.Exception? :)
Memet Olsen
4
@nawfal Dokładniej, jedyne typy wartości , dla których constmożna stosować, są sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, plus wszelkie enumrodzaje. constnie można użyć dla innych typów wartości, takich jak DateTimelub TimeSpanlub BigInteger. Nie można go również użyć do IntPtrstruktury (przez niektórych uważany za „prymitywny”; termin typ prymitywny jest mylący w języku C #). ↵↵ constMożna stosować dla wszystkich typów referencji . Jeśli typ jest string, można podać dowolną wartość ciągu. W przeciwnym razie wartość musi wynosić null.
Jeppe Stig Nielsen
@JeppeStigNielsen - Niedawno miałem kłótnię z serwerem na ten temat - wskazał, że możesz zrobić wszystko (wartości i typy referencyjne) constza pomocą default. W przypadku structtypów jest to instancja ze wszystkimi członkami ustawionymi na wartości domyślne.
Wai Ha Lee
28

Statyczny tylko do odczytu : Wartość można zmienić za pomocą statickonstruktora w czasie wykonywania. Ale nie poprzez funkcję członka.

Stała : domyślnie static. Wartości nie można zmienić z dowolnego miejsca (Ctor, funkcja, środowisko uruchomieniowe itp. Nigdzie).

Tylko do odczytu : Wartość można zmienić za pomocą konstruktora w czasie wykonywania. Ale nie poprzez funkcję członka.

Możesz zobaczyć moje typy repozytoriów: C # .

Yeasin Abedin Siam
źródło
1
Złe wieści ... uszkodzony link!
Fer R
Dobre fragmenty Siam ভাই :)
Muhammad Ashikuzzaman,
25

Słowo readonlykluczowe różni się od constsłowa kluczowego. constPole może zostać zainicjowany wyłącznie w deklaracji pola. readonlyPole może być zainicjowana albo w deklaracji lub w konstruktorze. Dlatego readonlypola mogą mieć różne wartości w zależności od zastosowanego konstruktora. Ponadto, chociaż constpole jest stałą czasową kompilacji, readonlypole może być używane do stałych środowiska wykonawczego

Krótkie i jasne odniesienie do MSDN tutaj

yazanpro
źródło
16

consti readonlysą podobne, ale nie są dokładnie takie same.

constPole jest stałą czasu kompilacji, co oznacza, że wartość ta może być obliczana w czasie kompilacji. readonlyPole umożliwia dodatkowe scenariusze, w których należy przeprowadzić kilka kod podczas budowy tego typu. Po zakończeniu budowy readonlypola nie można zmienić.

Na przykład constczłonków można używać do definiowania członków, takich jak:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

Ponieważ wartości takie jak 3.14 i 0 są stałymi czasów kompilacji. Rozważ jednak przypadek, w którym definiujesz typ i chcesz podać niektóre jego wcześniejsze wersje. Na przykład, możesz chcieć zdefiniować klasę Kolor i podać „stałe” dla typowych kolorów, takich jak Czarny, Biały itp. Nie można tego zrobić z elementami stałymi, ponieważ prawa strona nie jest stała czasu kompilacji. Można to zrobić za pomocą zwykłych elementów statycznych:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red   = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

Ale nie ma nic, co powstrzymałoby klienta Color od zepsucia się nim, być może poprzez zamianę wartości czerni i bieli. Nie trzeba dodawać, że spowodowałoby to konsternację u innych klientów klasy Color. Funkcja „tylko do odczytu” rozwiązuje ten scenariusz.

Po prostu wprowadzając readonlysłowo kluczowe w deklaracjach, zachowujemy elastyczną inicjalizację, jednocześnie zapobiegając pomijaniu kodu klienta.

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red   = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

Warto zauważyć, że stałe elementy są zawsze statyczne, podczas gdy element tylko do odczytu może być statyczny lub nie, tak jak zwykłe pole.

Możliwe jest użycie jednego słowa kluczowego do tych dwóch celów, ale prowadzi to do problemów z wersją lub problemów z wydajnością. Załóżmy przez chwilę, że użyliśmy do tego jednego słowa kluczowego (const), a programista napisał:

public class A
{
    public static const C = 0;
}

a inny programista napisał kod, który polegał na A:

public class B
{
    static void Main() => Console.WriteLine(A.C);
}

Czy generowany kod może polegać na tym, że AC jest stałą czasową kompilacji? Czyli użycie AC można po prostu zastąpić wartością 0? Jeśli powiesz „tak”, oznacza to, że twórca A nie może zmienić sposobu inicjalizacji AC - wiąże to ręce twórcy A bez pozwolenia.

Jeśli powiesz „nie” w tym pytaniu, ważna optymalizacja zostanie pominięta. Być może autor A jest przekonany, że AC zawsze będzie wynosić zero. Użycie zarówno const, jak i readonly pozwala twórcy A określić intencję. To zapewnia lepsze zachowanie wersjonowania, a także lepszą wydajność.

Ramesh Rajendran
źródło
12

Wolę używać const, kiedy tylko mogę, co, jak wspomniano powyżej, ogranicza się do dosłownych wyrażeń lub czegoś, co nie wymaga oceny.

Jeśli dojdę do tego ograniczenia, powrócę do statycznego odczytu tylko z jednym zastrzeżeniem. Na ogół używałbym publicznej własności statycznej z geterem i prywatnym statycznym polem tylko do odczytu, jak wspomina tutaj Marc .

Peter Meyer
źródło
7

Const: Const to nic innego jak „stała”, zmienna, której wartość jest stała, ale w czasie kompilacji. I przypisanie mu wartości jest obowiązkowe. Domyślnie const jest statyczny i nie możemy zmienić wartości zmiennej const w całym programie.

Statyczny tylko do odczytu: Wartość zmiennej typu Tylko do odczytu statycznego można przypisać w czasie wykonywania lub przypisać w czasie kompilacji i zmienić w czasie wykonywania. Ale wartość tej zmiennej można zmienić tylko w konstruktorze statycznym. I nie można go dalej zmieniać. Może się zmienić tylko raz w czasie wykonywania

Odniesienie: c-sharpcorner

mayank
źródło
6

Statyczne pole tylko do odczytu jest korzystne, gdy udostępnia się innym zestawom wartość, która może ulec zmianie w późniejszej wersji.

Załóżmy na przykład, że zespół Xodsłania stałą w następujący sposób:

public const decimal ProgramVersion = 2.3;

Jeśli zestaw Yodwołuje się Xi używa tej stałej, wartość 2.3 zostanie upieczona w zestawieY po skompilowaniu. Oznacza to, że jeśli Xpóźniej zostanie ponownie skompilowany ze stałą ustawioną na 2.4, Ynadal będzie używać starej wartości 2.3, aż do Yponownej kompilacji. Statyczne pole tylko do odczytu pozwala uniknąć tego problemu.

Innym sposobem patrzenia na to jest to, że jakakolwiek wartość, która może się zmienić w przyszłości, nie jest stała z definicji i dlatego nie powinna być reprezentowana jako jedna.

Yagnesh Cangi
źródło
3

const:

  1. wartość należy podać przy deklaracji
  2. stała czasowa kompilacji

tylko czytać:

  1. wartość może być podana podczas deklaracji lub podczas działania przy użyciu konstruktorów. Wartość może się różnić w zależności od użytego konstruktora.
  2. stała czasowa
dasumohan89
źródło
3

Const : const wartości zmiennych muszą zostać zdefiniowane wraz z deklaracją, a następnie nie ulegną zmianie. const są domyślnie statyczne, więc bez tworzenia instancji klasy możemy uzyskać do nich dostęp. ma to wartość w czasie kompilacji

ReadOnly : tylko zmienne wartości, które możemy zdefiniować podczas deklarowania, a także przy użyciu konstruktora w czasie wykonywania. Zmienne tylko do odczytu nie mogą uzyskać dostępu bez wystąpienia klasy.

Statyczny tylko do odczytu : statyczne wartości tylko do odczytu, które możemy zdefiniować podczas deklarowania, a także tylko za pomocą konstruktora statycznego, ale nie za pomocą żadnego innego konstruktora. Do tych zmiennych możemy również uzyskać dostęp bez tworzenia instancji klasy (jako zmienne statyczne).

statyczny tylko do odczytu będzie lepszym wyborem, jeśli będziemy musieli wykorzystywać zmienne w różnych zestawach. Sprawdź pełne szczegóły w poniższym linku

https://www.stum.de/2009/01/14/const-strings-a-very-convenient-way-to-shoot-yourself-in-the-foot/

użytkownik1756922
źródło
Czy możesz mi powiedzieć, dlaczego głosowałeś za odpowiedzią, abym mógł zaktualizować siebie, a także tutaj.
user1756922
Nie DV, ale być może ta odpowiedź tak naprawdę nie dodaje niczego do już wyczerpujących odpowiedzi tutaj.
Marc L.
pamiętajmy, że w Javie pod koniec lat 90. mieliśmy w projekcie wiele osób produkujących różne słoiki z plikami klas, które współpracowały ze sobą (odwoływały się do siebie) i publiczny ciąg const miał problemy z wersją, ponieważ były kopiowane
George Birbilis
2

Istnieje niewielka różnica między stałymi a statycznymi polami tylko do odczytu w języku C # .Net

const musi być zainicjowany wartością w czasie kompilacji.

const jest domyślnie statyczny i musi być inicjalizowany ze stałą wartością, której nie można później modyfikować. Nie można go używać ze wszystkimi typami danych. Dla ex-DateTime. Nie można go używać z typem danych DateTime.

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public static readonly string Name = string.Empty; //No error, legal

tylko do odczytu można zadeklarować jako statyczne, ale nie konieczne. Nie ma potrzeby inicjowania w momencie deklaracji. Jego wartość można przypisać lub zmienić za pomocą konstruktora jeden raz. Istnieje więc możliwość jednorazowej zmiany wartości pola tylko do odczytu (nie ma znaczenia, czy jest ono statyczne, czy nie), co nie jest możliwe w przypadku const.

Chirag
źródło
0

Stałe są jak sama nazwa wskazuje, pola, które się nie zmieniają i zwykle są definiowane statycznie w czasie kompilacji w kodzie.

Zmienne tylko do odczytu to pola, które mogą się zmieniać w określonych warunkach.

Można je zainicjować, gdy zadeklarujesz je po raz pierwszy jako stałą, ale zwykle są one inicjowane podczas budowy obiektu wewnątrz konstruktora.

Nie można ich zmienić po inicjalizacji w wyżej wymienionych warunkach.

Statyczny tryb „tylko do odczytu” wydaje mi się złym wyborem, ponieważ jeśli jest statyczny i nigdy się nie zmienia, więc użyj go jako const, jeśli może się zmienić, to nie jest stały, a następnie, w zależności od potrzeb, możesz użyć odczytu -tylko lub tylko zmienna regularna.

Kolejnym ważnym rozróżnieniem jest to, że stała należy do klasy, a zmienna tylko do odczytu należy do instancji!

Claudiu Cojocaru
źródło
0

Stała (określana w czasie kompilacji) może być używana w przypadkach, w których statyczny tylko do odczytu nie może, na przykład w instrukcjach switch lub konstruktorach atrybutów. Jest tak, ponieważ pola tylko do odczytu są rozwiązywane tylko w czasie wykonywania, a niektóre konstrukcje kodu wymagają zapewnienia czasu kompilacji. Tylko konstruktor może być obliczony w konstruktorze, co często jest istotną i przydatną rzeczą. Różnica jest funkcjonalna, tak jak moim zdaniem powinno być ich użycie.

Pod względem alokacji pamięci, przynajmniej w przypadku łańcuchów znaków (będących typem odniesienia), wydaje się, że nie ma różnicy w tym, że oba są internowane i będą odwoływały się do jednej internowanej instancji.

Osobiście mój domyślny jest tylko statyczny, ponieważ ma dla mnie bardziej semantyczny i logiczny sens, zwłaszcza, że ​​większość wartości nie jest potrzebna w czasie kompilacji. Nawiasem mówiąc, publiczna statystyka tylko do odczytu nie jest niczym niezwykłym ani niczym niezwykłym, jak zaznaczono w zaznaczonej odpowiedzi: na przykład System.String.Emptyjest jedna.

DvS
źródło
0

Inną różnicą między deklarowaniem const a statycznym tylko do odczytu jest przydział pamięci.

Pole statyczne należy do typu obiektu, a nie do instancji tego typu. W rezultacie, po odwołaniu do klasy po raz pierwszy, pole statyczne „żyje” w pamięci przez resztę czasu, a do tego samego wystąpienia pola statycznego będą się odnosić wszystkie instancje tego typu.

Z drugiej strony stałe pole „należy do instancji typu.

Jeśli ważniejsza jest dla ciebie pamięć delokalizacji, wolisz użyć const . Jeśli prędkość, użyj statycznego tylko do odczytu .

Boris Lipschitz
źródło