Буквальная нотация для словаря в 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 #?

pimvdb
источник
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()метод.

BoltClock
источник
2
Спасибо. Можно ли убрать Dictionary<string, string>хотя бы одно ? Это кажется излишним, но я могу ошибаться. Изменить: Это кажется более предпочтительным способом, спасибо.
pimvdb
3
@pimvdb: Да, вы можете: объявить его как var, компилятор выведет тип из new. Я отредактировал свой ответ.
BoltClock
7
Обратите внимание, что это не буквальная запись, строго говоря ... это просто ярлык для инициализации. Только строковые и некоторые примитивные типы имеют буквальное представление
Томас Левеск
Если вы имеете в виду, что хотите удалить одну из «строк» ​​- они не являются избыточными - одна для типа ключа, другая для типа значения. Специфических литералов для словарей не существует, обозначения, используемые в этом ответе, являются общими для всех классов с методом Add, который принимает две строки (и реализует IEnumerable).
Маркус Джонссон
1
@Markus Johnsson: Он имел в виду буквально Dictionary<string, string>. Мой код первоначально объявил тип, я просто изменил его varпосле его комментария.
BoltClock
13

Хотя ответ инициализатора словаря является полностью правильным, есть другой подход, на который я хотел бы обратить внимание (но я бы не рекомендовал его). Если ваша цель - обеспечить краткое использование API, вы можете использовать анонимные объекты.

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

В этом случае переменная «data» имеет «невыразимый» анонимный тип, так что вы можете передать это только как System.Object. Затем вы можете написать код, который может преобразовать анонимный объект в словарь. Такой код будет опираться на рефлексию, которая потенциально будет медленной. Однако вы можете использовать System.Reflection.Emitили System.Linq.Expressionsкомпилировать и кэшировать делегат, который сделает последующие вызовы намного быстрее.

API MVC Asp.net используют эту технику во многих местах, которые я видел. Многие Html-помощники имеют перегрузки, которые принимают либо объект, либо словарь. Я предполагаю, что цель их API-дизайна такая же, как и вы; краткий синтаксис при вызове метода.

MarkPflug
источник
1
Это поможет только тогда, когда набор ключей статичен. Вы также можете использовать кортежи для той же цели.
Сехе
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 ).

Изменить: это предложение в настоящее время отклонено :

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

aloisdg переходит на codidact.com
источник
1
Отличная идея, надеюсь, что она это делает, кажется, ежу понятно
jjxtra