Powodem jest to, że constmożna go zastosować tylko do pola, którego wartość jest znana w czasie kompilacji. Inicjalizator tablicy, który pokazałeś, nie jest stałym wyrażeniem w języku C #, więc powoduje błąd kompilatora.
Zadeklarowanie go readonlyrozwiązuje ten problem, ponieważ wartość nie jest inicjowana do czasu wykonania (chociaż gwarantuje się, że zainicjowała się przed pierwszym użyciem tablicy).
W zależności od tego, co ostatecznie chcesz osiągnąć, możesz również rozważyć deklarację wyliczenia:
Zauważ, że tablica tutaj nie jest oczywiście tylko do odczytu; Tytuły [2] = „walijski”; działałoby dobrze w środowisku uruchomieniowym
Marc Gravell
46
Prawdopodobnie chcesz też, aby był statyczny
tymtam
4
co powiesz na zadeklarowanie tablicy „const” w ciele metody, a nie w klasie?
serhio
19
Przepraszam za głosowanie w dół, ale const oznacza również statyczność. Zadeklarowanie tablicy jako tylko do odczytu nie jest zbliżone do obejścia. musi readonly staticmieć dowolne podobieństwo do żądanej semantyki.
Anton
3
@Anton, czy ty i twoi „obserwatorzy” usunęliście głosowanie? Dla mnie staticnie jest wymagane, aby działało, po prostu dodaje możliwość odwoływania się Titlesbez instancji, ale usuwa możliwość zmiany wartości dla różnych instancji (np. Możesz mieć konstruktor z parametrem w zależności od tego, którą wartość zmieniasz w tym readonlypolu).
Sinatr
57
Możesz zadeklarować tablicę jako readonly, ale pamiętaj, że możesz zmienić element readonlytablicy.
W .NET 4.5 i nowszych można zadeklarować listę jako IReadOnlyList <ciąg> zamiast IList <ciąg>.
Grzegorz Smulko
Żeby było jasne, nadal możesz zmieniać wartości w IReadOnlyList (po prostu nie dodawaj ani nie usuwaj elementów). Ale tak, zadeklarowanie go jako IReadOnlyList byłoby lepsze niż IList.
KevinVictor
Pytanie dotyczy constNIE readonly...
Yousha Aleayoub
50
Nie można utworzyć tablicy „const”, ponieważ tablice są obiektami i można je tworzyć tylko w czasie wykonywania, a stałe encje są rozwiązywane w czasie kompilacji.
Zamiast tego możesz zadeklarować tablicę jako „tylko do odczytu”. Ma to taki sam efekt jak const, z tą różnicą, że wartość można ustawić w czasie wykonywania. Można go ustawić tylko raz, a następnie jest to wartość tylko do odczytu (tj. Stała).
W tym poście podkreślono, dlaczego tablic nie można zadeklarować jako stałe
Radderz,
1
Możliwe jest zadeklarowanie stałej tablicy; problemem jest zainicjowanie go stałą wartością. Jedynym działającym przykładem, który przychodzi mi na myśl, jest const int[] a = null;niezbyt przydatny, ale rzeczywiście przykład stałej tablicowej.
waldrumpus
TO jest jedyna poprawna odpowiedź.
Yousha Aleayoub
17
Od wersji C # 6 możesz pisać w następujący sposób:
Spowoduje to utworzenie właściwości statycznej tylko do odczytu, ale nadal pozwoli ci zmienić zawartość zwracanej tablicy, ale gdy ponownie wywołasz właściwość, otrzymasz ponownie oryginalną, niezmienioną tablicę.
Dla wyjaśnienia, ten kod jest taki sam jak (lub w rzeczywistości skrót):
Należy pamiętać, że takie podejście ma wadę: nowa tablica jest tworzona przy każdym odwołaniu, więc jeśli używasz bardzo dużej tablicy, może to nie być najbardziej wydajne rozwiązanie. Ale jeśli ponownie użyjesz tej samej tablicy (na przykład umieszczając ją w prywatnym atrybucie), ponownie otworzy ona możliwość zmiany zawartości tablicy.
Jeśli chcesz mieć niezmienną tablicę (lub listę), możesz również użyć:
Jaki jest zysk z używania nieruchomości zamiast pola w tym przypadku?
nicolay.anykienko
2
Pole nie może zwrócić nowego obiektu przy każdym wywołaniu. Właściwość jest w zasadzie rodzajem „ukrytej funkcji”.
mjepson,
1
Jeśli odwoływałeś się do ostatniej opcji, można to zrobić za pomocą pola lub właściwości, ale ponieważ jest ona publiczna, wolę właściwość. Nigdy nie używam pola publicznego od czasu wprowadzenia właściwości.
mjepson,
1
Jest jeszcze jedna wada pierwszego podejścia: błąd kompilacji nie zostanie wyświetlony, jeśli Titles[0]na przykład zostaniesz przypisany - w efekcie próba przypisania jest cicho ignorowana. W połączeniu z nieefektywnością ponownego tworzenia tablicy za każdym razem zastanawiam się, czy takie podejście jest w ogóle warte. Natomiast drugie podejście jest wydajne i musisz zejść z drogi, aby pokonać niezmienność.
mklement0
6
Jeśli zadeklarujesz tablicę za interfejsem IReadOnlyList, otrzymasz stałą tablicę o stałych wartościach deklarowanych w czasie wykonywania:
using System.Collections.ObjectModel;// ...publicReadOnlyCollection<string>Titles{get;}=newReadOnlyCollection<string>(newstring[]{"German","Spanish","Corrects","Wrongs"});
Uwaga: Biorąc pod uwagę, że kolekcja jest stała koncepcyjnie, sensowne może staticbyć zadeklarowanie jej na poziomie klasy .
Powyższe:
Inicjuje niejawne pole zaplecza właściwości jeden raz z tablicą.
Zauważ, że { get; }- tj. Deklarowanie tylko modułu pobierania właściwości - powoduje, że sama właściwość jest domyślnie tylko do odczytu (próba połączenia readonlyz nią { get; }jest w rzeczywistości błędem składni).
Alternatywnie, możesz po prostu pominąć { get; }i dodać, readonlyaby utworzyć pole zamiast właściwości, jak w pytaniu, ale ujawnianie publicznych elementów danych jako właściwości zamiast pól jest dobrym nawykiem do tworzenia.
Tworzy array- jak struktury (pozwalając indeksowanego dostępu ), który jest naprawdę solidnie i tylko do odczytu (koncepcyjnie stały, raz utworzone), zarówno w odniesieniu do:
zapobieganie modyfikacji kolekcji jako całości (na przykład poprzez usunięcie lub dodanie elementów lub przypisanie nowej kolekcji do zmiennej).
zapobieganie modyfikacji poszczególnych elementów .
(Nawet pośrednia zmiana nie jest to możliwe - w przeciwieństwie z IReadOnlyList<T>roztworu, gdzie obsada może być wykorzystywane w celu uzyskania dostępu do zapisu elementów , jak pokazano na pomocne odpowiedzi mjepsen męska .
Ta sama luka dotyczy w interfejsie , który, mimo podobieństwa w nazwie do klasy , nie obsługuje nawet dostępu indeksowanego , przez co zasadniczo nie nadaje się do zapewniania dostępu podobnego do tablicy).(string[]) IReadOnlyCollection<T>ReadOnlyCollection
@mortb: Niestety IReadOnlyCollectionnie obsługuje dostępu indeksowanego, więc nie można go tutaj użyć. Dodatkowo, podobnie jak IReadOnlyList(który ma dostęp zindeksowany) jest podatny na manipulowanie elementami poprzez rzutowanie z powrotem na string[]. Innymi słowy: ReadOnlyCollection(na które nie można rzutować tablicy ciągów) jest najbardziej niezawodnym rozwiązaniem. Nieużywanie gettera jest opcją (i zaktualizowałem odpowiedź, aby to zauważyć), ale w przypadku danych publicznych prawdopodobnie lepiej trzymać się właściwości.
mklement0
5
Na moje potrzeby określam statictablicę zamiast niemożliwej consti działa ona:
public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Proste usunięcie constz przykładu OP również działa, ale to (lub twoja odpowiedź) pozwala zmienić zarówno: Titlesinstancję, jak i dowolną wartość. Jaki jest sens tej odpowiedzi?
Sinatr
@ Sinatr, odpowiedziałem na to 3 lata temu, kiedy zacząłem pracować w C #. Zostawiłem to, teraz jestem w świecie Java. Być może zapomniałem dodaćreadonly
ALZ
Po chwili zastanowienia, odpowiedź jest bezpośrednim jak zrobić kod OP pracy , bez żadnych const/ readonlyrozważania, po prostu dzięki czemu działa (jak gdyby constbył to błąd składni). Dla niektórych osób wydaje się to być cenną odpowiedzią (być może próbowali również constprzez pomyłkę?).
Sinatr
5
Możesz zastosować inne podejście: zdefiniuj stały ciąg reprezentujący tablicę, a następnie podziel ciąg na tablicę, gdy jej potrzebujesz, np.
Myślę, że koszt przeprowadzenia podziału znacznie przewyższa wszelkie korzyści wynikające z definicji const. Ale +1 za wyjątkowe podejście i nieszablonowe myślenie! ;)
Radderz
Już miałem opublikować to samo rozwiązanie, a potem zobaczyłem to, w przeciwieństwie do ostrych i negatywnych uwag, było to idealne rozwiązanie w moim scenariuszu, w którym musiałem przekazać const do atrybutu, a następnie podzieliłem wartość w konstruktorze atrybutów na dostać to, czego potrzebowałem. i nie widzę powodu, dla którego będzie to miało koszt wydajności, ponieważ atrybuty nie są tworzone dla każdej instancji.
Kalpesh Popat
4
W trosce o kompletność, teraz mamy do dyspozycji również ImmutableArrays. To powinno być naprawdę niezmienne:
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;publicReadOnlyCollection<string>Titles{get{returnnewList<string>{"German","Spanish","Corrects","Wrongs"}.AsReadOnly();}}
Jest to bardzo podobne do robienia tablicy tylko do odczytu.
Możesz to po prostu zrobić jako public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; nie ma potrzeby ponownego tworzenia listy przy każdym pobieraniu, jeśli i tak zostanie to ReadOnlyCollection.
Nyerguds,
3
To jedyna poprawna odpowiedź. Obecnie nie możesz tego zrobić.
Wszystkie pozostałe odpowiedzi sugerują użycie statycznych zmiennych tylko do odczytu, które są podobne , ale nie takie same jak stała. Stała jest na stałe zakodowana w zestawie. Statyczną zmienną tylko do odczytu można ustawić raz, prawdopodobnie podczas inicjalizacji obiektu.
Tablice są prawdopodobnie jedną z tych rzeczy, które można ocenić tylko w czasie wykonywania. Stałe muszą być oceniane w czasie kompilacji. Spróbuj użyć „tylko do odczytu” zamiast „const”.
Alternatywnie, aby obejść problem z elementami, które można modyfikować za pomocą tablicy tylko do odczytu, można zamiast tego użyć właściwości statycznej. (Poszczególne elementy można nadal zmieniać, ale zmiany te zostaną wprowadzone tylko w lokalnej kopii tablicy).
Odpowiedzi:
Tak, ale musisz to zadeklarować
readonly
zamiastconst
:Powodem jest to, że
const
można go zastosować tylko do pola, którego wartość jest znana w czasie kompilacji. Inicjalizator tablicy, który pokazałeś, nie jest stałym wyrażeniem w języku C #, więc powoduje błąd kompilatora.Zadeklarowanie go
readonly
rozwiązuje ten problem, ponieważ wartość nie jest inicjowana do czasu wykonania (chociaż gwarantuje się, że zainicjowała się przed pierwszym użyciem tablicy).W zależności od tego, co ostatecznie chcesz osiągnąć, możesz również rozważyć deklarację wyliczenia:
źródło
readonly static
mieć dowolne podobieństwo do żądanej semantyki.static
nie jest wymagane, aby działało, po prostu dodaje możliwość odwoływania sięTitles
bez instancji, ale usuwa możliwość zmiany wartości dla różnych instancji (np. Możesz mieć konstruktor z parametrem w zależności od tego, którą wartość zmieniasz w tymreadonly
polu).Możesz zadeklarować tablicę jako
readonly
, ale pamiętaj, że możesz zmienić elementreadonly
tablicy.Rozważ użycie enum, jak sugerował Cody, lub IList.
źródło
const
NIEreadonly
...Nie można utworzyć tablicy „const”, ponieważ tablice są obiektami i można je tworzyć tylko w czasie wykonywania, a stałe encje są rozwiązywane w czasie kompilacji.
Zamiast tego możesz zadeklarować tablicę jako „tylko do odczytu”. Ma to taki sam efekt jak const, z tą różnicą, że wartość można ustawić w czasie wykonywania. Można go ustawić tylko raz, a następnie jest to wartość tylko do odczytu (tj. Stała).
źródło
const int[] a = null;
niezbyt przydatny, ale rzeczywiście przykład stałej tablicowej.Od wersji C # 6 możesz pisać w następujący sposób:
Zobacz także: C #: Nowy i ulepszony C # 6.0 (konkretnie rozdział „Funkcje i właściwości wyrażania”)
Spowoduje to utworzenie właściwości statycznej tylko do odczytu, ale nadal pozwoli ci zmienić zawartość zwracanej tablicy, ale gdy ponownie wywołasz właściwość, otrzymasz ponownie oryginalną, niezmienioną tablicę.
Dla wyjaśnienia, ten kod jest taki sam jak (lub w rzeczywistości skrót):
Należy pamiętać, że takie podejście ma wadę: nowa tablica jest tworzona przy każdym odwołaniu, więc jeśli używasz bardzo dużej tablicy, może to nie być najbardziej wydajne rozwiązanie. Ale jeśli ponownie użyjesz tej samej tablicy (na przykład umieszczając ją w prywatnym atrybucie), ponownie otworzy ona możliwość zmiany zawartości tablicy.
Jeśli chcesz mieć niezmienną tablicę (lub listę), możesz również użyć:
Ale nadal wiąże się to z ryzykiem zmian, ponieważ nadal możesz rzutować go z powrotem na ciąg [] i modyfikować zawartość jako taką:
źródło
Titles[0]
na przykład zostaniesz przypisany - w efekcie próba przypisania jest cicho ignorowana. W połączeniu z nieefektywnością ponownego tworzenia tablicy za każdym razem zastanawiam się, czy takie podejście jest w ogóle warte. Natomiast drugie podejście jest wydajne i musisz zejść z drogi, aby pokonać niezmienność.Jeśli zadeklarujesz tablicę za interfejsem IReadOnlyList, otrzymasz stałą tablicę o stałych wartościach deklarowanych w czasie wykonywania:
Dostępne w .NET 4.5 i wyższych.
źródło
Rozwiązanie .NET Framework v4.5 +, które poprawia odpowiedź tdbeckett :
Uwaga: Biorąc pod uwagę, że kolekcja jest stała koncepcyjnie, sensowne może
static
być zadeklarowanie jej na poziomie klasy .Powyższe:
Inicjuje niejawne pole zaplecza właściwości jeden raz z tablicą.
Zauważ, że
{ get; }
- tj. Deklarowanie tylko modułu pobierania właściwości - powoduje, że sama właściwość jest domyślnie tylko do odczytu (próba połączeniareadonly
z nią{ get; }
jest w rzeczywistości błędem składni).Alternatywnie, możesz po prostu pominąć
{ get; }
i dodać,readonly
aby utworzyć pole zamiast właściwości, jak w pytaniu, ale ujawnianie publicznych elementów danych jako właściwości zamiast pól jest dobrym nawykiem do tworzenia.Tworzy array- jak struktury (pozwalając indeksowanego dostępu ), który jest naprawdę solidnie i tylko do odczytu (koncepcyjnie stały, raz utworzone), zarówno w odniesieniu do:
(Nawet pośrednia zmiana nie jest to możliwe - w przeciwieństwie z
IReadOnlyList<T>
roztworu, gdzie obsada może być wykorzystywane w celu uzyskania dostępu do zapisu elementów , jak pokazano na pomocne odpowiedzi mjepsen męska . Ta sama luka dotyczy w interfejsie , który, mimo podobieństwa w nazwie do klasy , nie obsługuje nawet dostępu indeksowanego , przez co zasadniczo nie nadaje się do zapewniania dostępu podobnego do tablicy).(string[])
IReadOnlyCollection<T>
ReadOnlyCollection
źródło
IReadOnlyCollection
nie obsługuje dostępu indeksowanego, więc nie można go tutaj użyć. Dodatkowo, podobnie jakIReadOnlyList
(który ma dostęp zindeksowany) jest podatny na manipulowanie elementami poprzez rzutowanie z powrotem nastring[]
. Innymi słowy:ReadOnlyCollection
(na które nie można rzutować tablicy ciągów) jest najbardziej niezawodnym rozwiązaniem. Nieużywanie gettera jest opcją (i zaktualizowałem odpowiedź, aby to zauważyć), ale w przypadku danych publicznych prawdopodobnie lepiej trzymać się właściwości.Na moje potrzeby określam
static
tablicę zamiast niemożliwejconst
i działa ona:public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
źródło
const
z przykładu OP również działa, ale to (lub twoja odpowiedź) pozwala zmienić zarówno:Titles
instancję, jak i dowolną wartość. Jaki jest sens tej odpowiedzi?readonly
const
/readonly
rozważania, po prostu dzięki czemu działa (jak gdybyconst
był to błąd składni). Dla niektórych osób wydaje się to być cenną odpowiedzią (być może próbowali równieżconst
przez pomyłkę?).Możesz zastosować inne podejście: zdefiniuj stały ciąg reprezentujący tablicę, a następnie podziel ciąg na tablicę, gdy jej potrzebujesz, np.
Takie podejście daje stałą, którą można zapisać w konfiguracji i w razie potrzeby przekształcić w tablicę.
źródło
W trosce o kompletność, teraz mamy do dyspozycji również ImmutableArrays. To powinno być naprawdę niezmienne:
Wymaga System.Collections.Immutable odniesienia NuGet
https://msdn.microsoft.com/en-us/library/mt452182(v=vs.111).aspx
źródło
Jest to sposób na robienie tego, co chcesz:
Jest to bardzo podobne do robienia tablicy tylko do odczytu.
źródło
public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();
; nie ma potrzeby ponownego tworzenia listy przy każdym pobieraniu, jeśli i tak zostanie to ReadOnlyCollection.To jedyna poprawna odpowiedź. Obecnie nie możesz tego zrobić.
Wszystkie pozostałe odpowiedzi sugerują użycie statycznych zmiennych tylko do odczytu, które są podobne , ale nie takie same jak stała. Stała jest na stałe zakodowana w zestawie. Statyczną zmienną tylko do odczytu można ustawić raz, prawdopodobnie podczas inicjalizacji obiektu.
Czasami są one wymienne, ale nie zawsze.
źródło
Wierzę, że możesz to zrobić tylko do odczytu.
źródło
Tablice są prawdopodobnie jedną z tych rzeczy, które można ocenić tylko w czasie wykonywania. Stałe muszą być oceniane w czasie kompilacji. Spróbuj użyć „tylko do odczytu” zamiast „const”.
źródło
Alternatywnie, aby obejść problem z elementami, które można modyfikować za pomocą tablicy tylko do odczytu, można zamiast tego użyć właściwości statycznej. (Poszczególne elementy można nadal zmieniać, ale zmiany te zostaną wprowadzone tylko w lokalnej kopii tablicy).
Oczywiście nie będzie to szczególnie wydajne, ponieważ za każdym razem tworzona jest nowa tablica ciągów.
źródło
Najlepsza alternatywa:
źródło