Sequence types

Learn how Ice sequences are mapped to C#

A field, an element in another sequence, or a value in a dictionary with a sequence type is mapped by default to an IList<T>.

T is the mapped C# type for the element type of the Ice sequence. For example:

ice
sequence<string> StringSeq;
sequence<Value> ClassSeq;
struct SequenceExample
{
StringSeq x;
ClassSeq y;
}
C#
public partial record struct SequenceExample
{
public required IList<string> X { get; set; }
public required IList<IceClass?> Y { get; set; }
}

By default, when the generated code decodes a sequence, it creates an array that is transmitted to you (the application) as an IList<T>. So if you need an array, you can safely cast this IList<T> to an array after decoding.

You can override this default mapping with the "cs:generic" metadata directive described below. "cs:generic" changes the mapping to the specified C# type with one special case: "cs:generic:List" keeps IList<T> as the formal type.

A sequence 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 sequences more convenient and occasionally faster.

Mapping for outgoing valuesDefault mapping for incoming values
ReadOnlyMemory<T>
T[]

You can override the default mapping with the "cs:generic" metadata directive; this gives you the C# type you specified for incoming values, and IEnumerable<T> for outgoing values.

Mapping for outgoing valuesDefault mapping for incoming values
IEnumerable<T>
T[]

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

You can use "cs:generic" metadata directive to customize the mapping for your sequence. This directive accepts a single argument:

  • List
  • LinkedList
  • Queue
  • Stack
  • or your own type name

You can change the default mapping for sequences to use a generic container provided by C#. For example:

ice
sequence<string> StringSeq;
sequence<Fruit> FruitSeq;
sequence<int> IntQueue;
sequence<double> DoubleStack;

The "cs:generic:<type>" metadata directive causes the Ice compiler to map the corresponding sequence to one of the containers in the System.Collections.Generic namespace. For example, the IntQueue sequence maps to System.Collections.Generic.Queue<int>.

Generic containers can be used for sequences of any element type except classes. For sequences of classes, only List is supported. Metadata that specifies any other generic type is ignored with a warning:

ice
class MyClass
{
// ...
}
sequence<MyClass> MyClassList; // OK
sequence<MyClass> MyClassLinkedList; // Ignored

In this example, sequence type MyClassList maps to the generic container System.Collections.Generic.List<MyClass>, but sequence type MyClassLinkedList uses the default mapping.

If the array mapping and the predefined containers are unsuitable for your application (for example, because you may need a priority queue, which is not a standard C# container), you can implement your own custom containers and direct the Ice compiler to map sequences to these custom containers. For example:

ice
sequence<int> Queue;

This metadata directive causes the Ice Queue sequence to be mapped to the type MyTypes.PriorityQueue. You must specify the fully-qualified name of your custom type following the cs:generic: prefix. This is because the generated code prepends a global:: qualifier to the type name you provide; for the preceding example, the generated code refers to your custom type as global::MyTypes.PriorityQueue<int>.

Your custom type can have whatever interface you deem appropriate, but it must meet the following requirements:

  • The custom type must implement System.Collections.Generic.IEnumerable<T>.
  • The custom type must provide a parameterless constructor.
  • The custom type must provide a Count property that returns the number of elements in the collection.
  • The custom type must provide an Add method that appends an element to the end of the collection.

As an example, here is a minimal class (omitting implementation) that meets these criteria:

C#
public class PriorityQueue<T> : IEnumerable<T>
{
public IEnumerator<T> GetEnumerator();
public int Count { get; }
public void Add(T element);
public PriorityQueue() { }
// Other methods and fields here...
}

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.