Dosłowna notacja słownika w języku C #?

182

Obecnie mam WebSocket między JavaScript a serwerem zaprogramowanym w C #. W JavaScript mogę łatwo przekazywać dane za pomocą tablicy asocjacyjnej:

var data = {'test': 'val',
            'test2': 'val2'};

Aby przedstawić ten obiekt danych po stronie serwera, używam Dictionary<string, string>, ale jest to bardziej „kosztowne pisanie” niż w JavaScript:

Dictionary<string, string> data = new Dictionary<string,string>();
data.Add("test", "val");
data.Add("test2", "val2");

Czy istnieje jakiś dosłowny zapis dla tablic asocjacyjnych Dictionaryw C #?

pimvdb
źródło
C # 9 zawiera propozycję literałów słownikowych. Poniżej zamieszczam odpowiedź na ten temat . Nasze marzenie się spełniło!
aloisdg przenosi się do codidact.com

Odpowiedzi:

291

Używasz składni inicjalizującej kolekcję , ale nadal musisz new Dictionary<string, string>najpierw utworzyć obiekt, ponieważ składnia skrótu jest tłumaczona na kilka Add()wywołań (takich jak kod):

var data = new Dictionary<string, string>
{
    { "test", "val" }, 
    { "test2", "val2" }
};

W C # 6 masz teraz możliwość użycia bardziej intuicyjnej składni ze słownikiem, a także dowolnego innego typu, który obsługuje indeksatory . Powyższe oświadczenie można przepisać jako:

var data = new Dictionary<string, string>
{
    ["test"] = "val",
    ["test2"] = "val2"
};

W przeciwieństwie do inicjatorów kolekcji, wywołuje to narzędzie ustawiające indeksowanie pod maską, a nie odpowiednią Add()metodę.

BoltClock
źródło
2
Dzięki. Czy można usunąć jedną z tych rzeczy Dictionary<string, string>? Wydaje się raczej zbędny, ale mogę się mylić. Edycja: To naprawdę wydaje się bardziej preferowany sposób, dziękuję.
pimvdb
3
@pimvdb: Tak, możesz: zadeklarować to jako var, kompilator wyprowadzi typ z new. Zredagowałem swoją odpowiedź.
BoltClock
7
Zauważ, że nie jest to dosłowny zapis, mówiąc ściśle ... to tylko skrót do inicjalizacji. Tylko ciąg znaków i niektóre prymitywne typy mają dosłowne przedstawienie
Thomas Levesque
Jeśli masz na myśli, że chcesz usunąć jeden z „ciągów” - nie są one zbędne - jeden jest dla typu klucza, a drugi dla typu wartości. Nie ma literału specyficznego dla słowników, notacja zastosowana w tej odpowiedzi jest ogólna dla wszystkich klas z metodą Add, która pobiera dwa łańcuchy (i implementuje IEnumerable).
Markus Johnsson,
1
@Markus Johnsson: Dosłownie miał na myśli Dictionary<string, string>. Mój kod pierwotnie zadeklarował typ, właśnie go zmieniłem na varjego komentarz.
BoltClock
13

Chociaż odpowiedź inicjalizująca słownik jest całkowicie poprawna, istnieje inne podejście do tego, na które chciałbym zwrócić uwagę (ale może nie polecam). Jeśli Twoim celem jest zapewnienie zwięzłego użycia interfejsu API, możesz użyć obiektów anonimowych.

var data = new { test1 = "val", test2 = "val2"};

Zmienna „data” jest wtedy „niewymownym” typem anonimowym, więc można ją przekazywać tylko jako System.Object. Następnie możesz napisać kod, który może przekształcić anonimowy obiekt w słownik. Taki kod opierałby się na refleksji, która potencjalnie byłaby powolna. Możesz jednak użyć System.Reflection.Emitlub System.Linq.Expressionsskompilować i buforować delegata, który znacznie szybciej wykona kolejne połączenia.

Interfejsy API Asp.net MVC wykorzystują tę technikę w wielu miejscach, które widziałem. Wiele pomocników HTML ma przeciążenia, które akceptują albo obiekt, albo słownik. Zakładam, że cel ich interfejsu API jest taki sam, jak tego, czego szukasz; krótka składnia w wywołaniu metody.

MarkPflug
źródło
1
Pomoże to tylko wtedy, gdy zestaw kluczy jest statyczny. Możesz także użyć Tuples do tego samego celu.
sehe
8

Korzystanie DynamicObjectz prostszego inicjatora słownika nie jest trudne.

Wyobraź sobie, że chcesz wywołać następującą metodę

void PrintDict(IDictionary<string, object> dict) {
    foreach(var kv in dict) {
        Console.WriteLine ("  -> " + kv.Key + " = " + kv.Value);
    }
}

używając dosłownej składni, takiej jak

var dict = Dict (Hello: "World", IAm: "a dictionary");
PrintDict (dict);

Można to osiągnąć, tworząc taki dynamiczny obiekt

dynamic Dict {
    get {
        return new DynamicDictFactory ();
    }
}

private class DynamicDictFactory : DynamicObject
{
    public override bool TryInvoke (InvokeBinder binder, object[] args, out object result)
    {
        var res = new Dictionary<string, object> ();
        var names = binder.CallInfo.ArgumentNames;

        for (var i = 0; i < args.Length; i++) {
            var argName = names [i];
            if(string.IsNullOrEmpty(argName)) throw new ArgumentException();
            res [argName] = args [i];
        }
        result = res;
        return true;
    }
}
Krumelur
źródło
6

Użyj literału słownika (propozycja C # 9)

C # 9 wprowadza prostszą składnię do tworzenia zainicjowanych Dictionary<TKey,TValue>obiektów bez konieczności określania nazwy typu Dictionary lub parametrów typu. Parametry typu dla słownika są wywnioskowane przy użyciu istniejących reguł używanych do wnioskowania o typie tablicy.

// C# 1..8    
var x = new Dictionary <string,int> () { { "foo", 4 }, { "bar", 5 }};   
// C# 9    
var x = ["foo":4, "bar": 5];  

Ta składnia upraszcza pracę ze słownikami w języku C # i usuwanie zbędnego kodu.

Możesz śledzić ten problem na GitHub (a oto kamień milowy dla C # 9 ).

Edycja: ta propozycja jest obecnie odrzucona :

[...] Uważamy, że istnieje wiele interesujących przypadków użycia przy inicjowaniu danych, szczególnie w przypadku niezmiennych słowników. Nie znajdujemy istniejącej składni do inicjowania słownika, która jest uciążliwa, ani nie widzimy jej jako częstego wzorca w kodzie, który mógłby wiele skorzystać z funkcji języka. Uważamy, że należy ponownie przyjrzeć się ogólnemu obszarowi inicjalizacji danych po wykonaniu zapisów i więdnięciu. [...]

aloisdg przechodzi na codidact.com
źródło
1
Świetny pomysł, mam nadzieję, że się uda, wydaje się niczym bez
zastanowienia