Буквене позначення для словника в C #?


182

На даний момент у мене є WebSocket між JavaScript та сервером, запрограмованим на C #. У JavaScript я можу легко передавати дані за допомогою асоціативного масиву:

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

Для представлення цього об'єкта даних на стороні сервера я використовую a Dictionary<string, string>, але це "набагато дорожче набору тексту", ніж у JavaScript:

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

Чи є якесь буквальне позначення для асоціативних масивів / Dictionaryс у C #?


C # 9 містить пропозицію для літературних словників. Відповідь про це я розміщую нижче . Наша мрія здійснилася!
aloisdg переходить на codidact.com

Відповіді:


291

Ви використовуєте синтаксис ініціалізатора колекції , але все-таки потрібно зробити new Dictionary<string, string>об'єкт спочатку, оскільки синтаксис швидкого доступу переведений на купу Add()дзвінків (наприклад, ваш код):

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

У C # 6 тепер у вас є можливість використовувати інтуїтивніший синтаксис зі словником, а також будь-який інший тип, який підтримує індексатори . Вищенаведене твердження можна переписати як:

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

На відміну від ініціалізаторів колекції, це викликає встановлення індексатора під кришкою, а не відповідний Add()метод.


2
Дякую. Чи можливо видалити один із них Dictionary<string, string>? Це здається зайвим, але я можу помилитися. Редагувати: Це здається більш кращим способом, дякую.
pimvdb

3
@pimvdb: Так, ви можете: оголосити це як a var, компілятор визначить тип з new. Я відредагував свою відповідь.
BoltClock

7
Зауважте, що це не буквальне позначення, строго кажучи ... це лише ярлик для ініціалізації. Лише рядкові та деякі примітивні типи мають буквальне представлення
Томас Левеск

Якщо ви маєте на увазі, що хочете видалити один із "рядкових" s - вони не є зайвими - один призначений для типу ключа, інший для типу значення. Немає прямої специфіки для словників, позначення, використовувані в цій відповіді, є загальними для всіх класів із методом Add, який займає два рядки (і реалізує IEnumerable).
Маркус Джонсон

1
@Markus Йонссон: Він мав в виду, в буквальному сенсі, Dictionary<string, string>. Мій код спочатку оголосив тип, я щойно змінив його varпісля його коментаря.
BoltClock

13

Незважаючи на те, що відповідь на ініціалізатор словника є абсолютно правильною, я би зазначив ще один підхід до цього (але я, можливо, не рекомендую). Якщо ваша мета полягає в наданні короткого використання API, ви можете використовувати анонімні об'єкти.

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

Змінна "дані" тоді є "несказаним" анонімним типом, тож ви можете пропустити це лише як System.Object. Потім ви можете написати код, який може перетворити анонімний об’єкт у словник. Такий код покладається на рефлексію, яка потенційно може бути повільною. Однак ви можете використовувати System.Reflection.Emitабо System.Linq.Expressionsдля компіляції та кешування делегата, який би зробив наступні дзвінки набагато швидше.

API API MVC використовують цю техніку в багатьох місцях, які я бачив. Багато помічників Html мають перевантаження, які приймають або об’єкт, або словник. Я припускаю, що мета їх розробки API така ж, як і після чого ви хочете; короткий синтаксис при виклику методу.


1
Це допоможе лише тоді, коли набір клавіш статичний. Ви також можете використовувати кортежі з тією ж метою.
sehe

8

Використовуючи DynamicObject, не так складно створити більш простий ініціалізатор словника.

Уявіть, що ви хочете викликати наступний метод

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

використовуючи буквальний синтаксис типу

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

Це можна досягти, створивши подібний динамічний об’єкт

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

6

Використовуйте словникові літерали (пропозиція C # 9)

C # 9 вводить простіший синтаксис для створення ініціалізованих Dictionary<TKey,TValue>об'єктів без необхідності вказувати ні ім'я типу словника, ні параметри типу. Параметри типу для словника виводяться з використанням існуючих правил, що використовуються для виводу типу масиву.

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

Цей синтаксис спрощує роботу зі словниками в C # та видаляє зайвий код.

Ви можете слідкувати за проблемою на GitHub (і ось віха для C # 9 ).

Редагувати: Ця пропозиція наразі відхилена :

[...] Ми вважаємо, що існує ряд цікавих випадків використання навколо ініціалізації даних, зокрема для таких речей, як незмінні словники. Ми не знаходимо існуючий синтаксис для ініціалізації словника, який є обтяжливим, і не вважаємо його частим шаблоном у коді, який би виграв від мовної функції. Нам важливо, що загальну область ініціалізації даних слід переглянути ще раз після того, як ми зробимо записи і холки. [...]


1
Відмінна ідея, сподіваюсь, що вона встигає, здається, що не
мандрівник
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.