Nie można użyć tablicy „inline” w języku C #?


92

Wyobraź sobie, że gdzieś to masz

public static T AnyOne<T>(this T[] ra) where T:class
    {
    int k = ra.Length;
    int r = Random.Range(0,k);
    return ra[r];
    }

a nawet tylko to

public static string OneOf(this string[] strings)
    {
    return "a";
    }

Wtedy oczywiście możesz to zrobić ...

string[] st = {"a","b","c"};
string letter = st.AnyOne();

... który jest świetny. ALE. Wygląda na to, że NIE możesz tego zrobić:

string letter = {"a","b","c"}.AnyOne();

a może rzeczywiście to

string letter = ( {"a","b","c"} ).AnyOne();

lub cokolwiek innego próbowałem.

W rzeczywistości (1) dlaczego nie można tego zrobić? i (2) czy czegoś mi brakuje, jak byś to zrobił, skoro jest sposób?


5
Nie jestem pewien, czy zduplikowane pytanie jest właściwe, OP nie pyta o inicjatory tablicy, ale dlaczego kompilator nie rozpozna obiektu jako tablicy, dopóki nie zostanie przypisany.
Ron Beyer,

4
Nie znam terminologii C #, ale uważam, że jest to częściej nazywane literałem lub literałem tablicowym, a nie wstawionym .
chi

3
Ten element składniowy jest inicjatorem tablicy lub inicjatorem kolekcji , w zależności od kontekstu, w którym jest używany. W żadnym przypadku nie jest to klasyfikowane jako wyrażenie .
Eric Lippert

Odpowiedzi:


133

Najpierw musisz utworzyć tablicę, używając new[].

string letter = (new[] {"a","b","c"}).AnyOne();

Jak wspomniał @hvd, możesz to zrobić bez parantez (..), dodałem je, ponieważ myślę, że są bardziej czytelne.

string letter = new[] {"a","b","c"}.AnyOne();

I możesz określić typ danych, new string[]tak jak w przypadku innych odpowiedzi.


Nie możesz tego zrobić {"a","b","c"}, ponieważ możesz o tym myśleć jako o sposobie zapełnienia tablicy, a nie jej tworzenia.

Innym powodem będzie to, że kompilator będzie zdezorientowany, nie będzie wiedział, co utworzyć, na przykład a string[]{ .. }lub a List<string>{ .. }.

Korzystanie z samego new[]kompilatora może wiedzieć, według typu danych ( ".."), pomiędzy {..}, czego chcesz ( string). Najważniejsze jest to [], że chcesz mieć tablicę.

Nie możesz nawet utworzyć pustej tablicy z new[].

string[] array = new []{ }; // Error: No best type found for implicity-typed array

13
Nie potrzebujesz tych nawiasów. string letter = new[] {"a","b","c"}.AnyOne();jest w porządku. Jeśli ich potrzebujesz, jeśli uważasz, że są bardziej czytelne dzięki nawiasom, są ważne, ale w tym przypadku myślę, że warto przynajmniej wspomnieć, że jest to świadomy wybór z Twojej strony, że nie został wymuszony przez język.

Wiem o składni new [] {1,2}, ale czy istnieje jeszcze prostsza składnia? Coś w rodzaju [1, 2]?
seguso

51

(1) dlaczego nie można tego zrobić? {"a","b","c"}.AnyOne();

Ta linia:

string[] st = {"a","b","c"};

to krótka ręka dla równoważnego wyrażenia tworzenia tablicy (pod ILSpy )

string[] st = new string[]  {"a","b","c"};

string[] st = {"a","b","c"} Można tego użyć tylko w momencie deklaracji , nie można go nie używać nigdzie indziej, nie można nawet tego zrobić:

string[] st;
st = {"a", "b", "c"}; //Error

Jest to wyjaśnione w sekcji 7.6.10.4 dla wyrażenia tworzenia tablicy w specyfikacjach języka C #.

Więc "{"a", "b", "c"}"samo to bez użycia w deklaracji nic nie znaczy. Dlatego nie można go używać z metodą rozszerzającą, ponieważ metoda rozszerzająca działa na tablicy.

(2) czy czegoś mi brakuje, jak byś to zrobił, skoro jest sposób?

Wspomniano w @ adricadar za odpowiedź , można to zrobić:

(new[] {"a","b","c"}).AnyOne();

lub

(new string[] {"a","b","c"}).AnyOne();

48

Odpowiadam na pytania „dlaczego nie”, ponieważ po pierwsze, odpowiedzi prawie nigdy nie są satysfakcjonujące - już otrzymałeś odpowiedź „funkcja nie jest taka, jakiej chcesz, ponieważ specyfikacja nie mówi tego, co chcesz, aby powiedzieć” , co, jak sądzę, nie było szczególnie satysfakcjonującą odpowiedzią. Po drugie, zespół projektantów nie musi uzasadniać, dlaczego świat nie jest taki, jaki chcesz; funkcje nie istnieją za darmo i są projektowane poza językiem; raczej funkcje muszą być najpierw uzasadnione, a następnie zaprojektowane.

Postarajmy się więc, aby Twoje pytanie „dlaczego nie” było trochę bardziej wyraźne. Istniejąca funkcja to „inicjalizator tablicy może być użyty (a) po prawej stronie równych w inicjalizacji lub (b) po prawej stronie konstrukcji obiektu typu tablica”. Proponowana funkcja to: „inicjalizator tablicy może być również użyty jako wyrażenie”. Pytanie brzmi: „jaką krytykę podałby Eric w stosunku do proponowanej funkcji?”

Pierwsza krytyka, którą chciałbym skierować, dotyczy tego, że nie jest jasne, jakiego rodzaju jest to wyrażenie. W inicjatorze zmiennej masz typ zmiennej, aw wyrażeniu tworzenia obiektu masz typ obiektu; z obu tych możemy wywnioskować typ skonstruowanej tablicy. Bez żadnej wskazówki, jaki typ powinniśmy wywnioskować?

W C # 1.0, kiedy ta funkcja została dodana, w języku było w sumie wnioskowanie o typie zerowym. We wczesnych latach C # zasadą projektowania było „bez niespodzianek”, a kompilator nie był „zbyt inteligentny”. Jeśli programista chce, aby wyrażenie było określonego typu, typ ten powinien być w pewnym sensie oczywisty w wyrażeniu. Kiedy powiesz

new double[] { 1, 2, 3.4 }

jest całkiem jasne, jaki typ jest przeznaczony. podobnie

new Animal[] { cat, dog, null }

Proponowana funkcja narusza tę zasadę. Wyrażenie musi mieć typ, ale w żadnym wypadku nie jest jasne, jaki jest typ argumentu

M({cat, dog, null})

Ponadto: załóżmy, że mamy dwa przeciążenia M, z których jeden przyjmuje tablicę, Animala drugi przyjmuje tablicę IPet. Które przeciążenie Mma zastosowanie? Czy jedna z konwersji jest lepsza od drugiej? Rodzaje elementów to Cati Dog; czy sensowne jest wywnioskowanie typu, który nawet tam nie występuje? To wszystkie pytania, na które zespół projektowy musi się zastanowić, i są to pytania, na które nie ma oczywistych odpowiedzi. Proponowana funkcja prowadzi nas w dość krótkim czasie na głębokie wody.

Teraz C # 3.0 rozwiązuje ten problem, ponieważ C # 3.0 dodał wiele funkcji, w których kompilator wnioskuje typy w imieniu dewelopera. Wcześniejsze zasady „bez niespodzianek” i „prostych reguł” były w konflikcie z innymi zasadami projektowania potrzebnymi do działania LINQ. Czy proponowana funkcja powinna zostać dodana w języku C # 3.0?

To mogło być. Funkcja faktycznie dodana w C # 3.0 to:

new[] { x, y, z }

wnioskuje o typ tablicy za pomocą algorytmu: weź wyrażenia dla elementów, które mają typy, określ, który z tych typów jest unikalnym, najbardziej ogólnym typem, na który wszystkie inne wyrażenia są konwertowane, a jeśli taki typ istnieje, wybierz go. W przeciwnym razie wystąpi błąd,

Ta funkcja mogła zostać jeszcze bardziej złagodzona, aby uczynić ją new[]opcjonalną. To nie zostało zrobione.

Teraz, gdybyś poprosił mnie w ramach czasowych C # 3.0 o skrytykowanie proponowanej funkcji, zwróciłbym uwagę, że (1) kompilator C # 3.0 był już w poważnym niebezpieczeństwie poślizgnięcia się harmonogramu dla całego wydania, więc nie dodawajmy więcej projekt, implementacja i testowanie całkowicie niepotrzebnej funkcji, która oszczędza użytkownikowi sześć naciśnięć klawiszy, a (2) C # 3.0 dodał także inicjatory kolekcji:

new List<int>() { 10, 20, 30 }

dlaczego ma być {10, 20, 30}automatycznie tablicą ? Dlaczego nie miałoby to być List<int>? Lub którykolwiek z wielu innych typów? Dlaczego tendencja do tablic? Pamiętaj, że kiedy już zdecydujemy się zapisać składnię tablic, utkniemy z nią na zawsze . Może to nigdy nie być nic innego, więc proponowana funkcja jest nie tylko niepotrzebna, ale także zapobiega możliwym przyszłym funkcjom, które wydają się prawdopodobne.

Podsumowując: proponowana funkcja bezpośrednio naruszyła niektóre zasady projektowania języka C # 1.0. Dodaje tylko niepotrzebne obciążenie do C # 3.0. We wszystkich wersjach języka, począwszy od C # 3.0, proponowana funkcja nie ma dobrego argumentu, aby zalecać poświęcanie na nią czasu, wysiłku i pieniędzy w porównaniu z wieloma innymi bardziej wartościowymi funkcjami.

Dlatego nie ma takiej funkcji.


Heh, musisz mieć koszulkę z nadrukiem "Świat nie jest taki, jaki chcesz, żeby był" :)
slugster

6
@JoeBlow: Po pierwsze, nie ma za co. Jeśli chodzi o „dlaczego nie” - Twój komentarz ładnie ilustruje problem. Kiedy niektórzy ludzie zadają pytanie „dlaczego”, szukają logicznego uzasadnienia . Niektórzy szukają pragmatycznego uzasadnienia . I najwyraźniej szukasz wiersza specyfikacji opisującego regułę . Jest tak niejasny, że bardzo trudno jest sformułować dobrą odpowiedź, która jest ukierunkowana na pytanie w rzeczywistości pytającego. Pytania „dlaczego nie” są jeszcze gorsze, ponieważ są niejasnymi pytaniami o rzeczy, które nawet nie istnieją .
Eric Lippert

3
@EricLippert To nawet gorsze niż tylko pytanie o rzeczy, które _ mogą_ istnieć : jeśli pracujesz z zespołem nad oprogramowaniem, cały zespół spędza każdy dzień roku na myśleniu o funkcjach i równoważeniu konsekwencji. Podejmowane są decyzje. Prośby o dodanie funkcji i „dlaczego” wymagają uzasadnienia. Jednak dlaczego nie oznacza, że ​​ktoś po prostu chce coś zrobić, co w zasadzie kwestionuje ocenę samego zespołu. Co gorsza, osoba zadająca pytanie „ dlaczego nie” zwykle niewiele wie na ten temat. W związku z tym uważam, że odrzucanie pytań dlaczego nie jest całkowicie sprawiedliwe. Dobra robota IMO.
atlaste
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.