Dictionary types

Learn how to define and use dictionaries in Slice.

A dictionary is a built-in generic type that represents an associative array. All keys of this associative array have the same Slice type, and all values have the same Slice type.

A dictionary is like a sequence of key-value pairs with the following constraints:

  • each key is unique
  • the type of the key is a string, bool, integral type, enum type, custom type, or a compact struct with key-compatible fields

You can construct a dictionary type inline, without giving it a name, for example to specify the type of a parameter or field:

slice
module VisitorCenter
interface Greeter {
greet(name: string) -> string
allPreviousGreetings() -> Dictionary<string, string>
}

A built-in generic type with type arguments, such as a Dictionary<string, string>, is called a constructed type.

You can use any Slice type for the value-type of your dictionary. For example:

A field, an element in a sequence, or a value in another dictionary with type Dictionary<K, V> is mapped to an IDictionary<TKey, TValue>.

TKey resp. TValue is the mapped C# type for the Slice key type resp. value type. For example:

By default, when the generated code decodes a dictionary, it creates a C# Dictionary<TKey, TValue> that is transmitted to you (the application) as an IDictionary<TKey, TValue>. You can safely cast this IDictionary<TKey, TValue> to a Dictionary<TKey, TValue> after decoding.

You can override this default with the cs::type attribute. This attribute only changes the type that the generated code uses during decoding to fill-in the field: the C# field type itself remains an IDictionary<TKey, TValue>.

A dictionary parameter has one mapping when it's sent and a different mapping when it's received. This distinction between incoming and outgoing values makes sending dictionaries more convenient and occasionally faster.

Mapping for outgoing valuesDefault mapping for incoming values
IEnumerable<KeyValuePair<TKey, TValue>>
Dictionary<TKey, TValue>

You can override the default mapping for incoming values with the cs::type attribute; this gives you the C# type you specified for incoming values. cs::type doesn't change the mapping for outgoing values.

You can use the cs::type attribute to customize the mapping of your dictionary. This attribute accepts a single string argument: the name of a type similar to Dictionary<TKey, TValue>.

More specifically, this type must provide a capacity constructor (with an int parameter). It must also implement IDictionary<TKey, TValue> when cs::type is applied to a field; it must implement ICollection<KeyValuePair<TKey, TValue>> when cs::type is applied to a parameter. For example:

slice
interface Greeter {
// List<KeyValuePair<TKey, TValue>> implements
// ICollection<KeyValuePair<TKey, TValue>>;
// it also provides a capacity constructor.
allPreviousGreetings() ->
[cs::type("List<KeyValuePair<string, string>>")] Dictionary<string, string>
}
C#
public partial interface IGreeter
{
Task<List<KeyValuePair<string, string>>>
AllPreviousGreetingsAsync(
IFeatureCollection? features = null,
CancellationToken cancellationToken = default);
}
public partial interface IGreeterService
{
ValueTask<IEnumerable<KeyValuePair<string, string>>>
AllPreviousGreetingsAsync(
IFeatureCollection features,
CancellationToken cancellationToken);
}

Was this page helpful?

CookiesYour privacy
This website uses cookies to analyze traffic and improve your experience.
By clicking "Accept," you consent to the use of these cookies. You can learn more about our cookies policy in our Privacy Policy.