Skip to main content

Collections

TinyCLR ships both the generic collections from modern .NET (List<T>, Dictionary<TKey, TValue>, HashSet<T>) and the classic non-generic types (ArrayList, Hashtable, Stack, Queue). Prefer the generic ones in new code — they provide compile-time type safety and avoid the boxing overhead of object-based storage.

For the full API surface, see Microsoft's collections documentation — the API matches the subset TinyCLR ships. A few less-common members are not included; see Limitations.

Generic collections

List<T>, Dictionary<TKey, TValue>, and HashSet<T> live in System.Collections.Generic.

List<T>

A dynamic, type-safe array — add, index, insert, remove, search, and sort:

using System.Collections.Generic;
using System.Diagnostics;

var readings = new List<int>();
readings.Add(10);
readings.Add(20);
readings.Add(30);

Debug.WriteLine("Count: " + readings.Count); // 3
Debug.WriteLine("First: " + readings[0]); // 10 (indexer get)
readings[1] = 99; // indexer set

readings.Insert(1, 15); // → 10, 15, 99, 30
readings.Remove(99); // remove first matching value
readings.RemoveAt(0); // remove by index

if (readings.Contains(30))
Debug.WriteLine("Index of 30: " + readings.IndexOf(30));

readings.Sort(); // ascending
readings.Sort((a, b) => b - a); // custom comparison: descending

var firstBig = readings.Find(x => x > 20); // first element matching a predicate

foreach (var v in readings)
Debug.WriteLine(v);

List<T> also supports FindAll, FindIndex, Exists, TrueForAll, Reverse, ToArray, and Clear.

Dictionary<TKey, TValue>

Fast key/value lookup. Keys are unique:

using System.Collections.Generic;
using System.Diagnostics;

var pinFunctions = new Dictionary<string, int>();
pinFunctions.Add("LED", 11);
pinFunctions["BUTTON"] = 7; // indexer-set adds the key if missing

Debug.WriteLine("LED pin: " + pinFunctions["LED"]); // 11

// TryGetValue avoids an exception when the key might be missing.
if (pinFunctions.TryGetValue("BUTTON", out var btn))
Debug.WriteLine("Button pin: " + btn);

if (pinFunctions.ContainsKey("LED"))
pinFunctions.Remove("LED");

foreach (var kv in pinFunctions)
Debug.WriteLine(kv.Key + " = " + kv.Value);
note
  • Add throws if the key already exists; the indexer (d[key] = value) overwrites instead.
  • Reading a missing key with the indexer throws KeyNotFoundException — use TryGetValue (or ContainsKey) when the key may be absent.
  • Enumeration order is not guaranteed. Don't rely on entries coming back in insertion order.

HashSet<T>

A set of unique values, with fast membership tests and the standard set operations:

using System.Collections.Generic;
using System.Diagnostics;

var seen = new HashSet<int>();
Debug.WriteLine(seen.Add(1)); // True — added
Debug.WriteLine(seen.Add(1)); // False — already present
seen.Add(2);
seen.Add(3);

Debug.WriteLine("Contains 2: " + seen.Contains(2)); // True
Debug.WriteLine("Count: " + seen.Count); // 3

// Set algebra against another set.
var other = new HashSet<int>();
other.Add(3);
other.Add(4);

seen.UnionWith(other); // seen → {1, 2, 3, 4}
seen.IntersectWith(other); // keep only shared → {3, 4}
Debug.WriteLine("Overlaps: " + seen.Overlaps(other));

HashSet<T> also has ExceptWith, SymmetricExceptWith, IsSubsetOf/IsSupersetOf, SetEquals, and RemoveWhere. Like Dictionary, enumeration order is not guaranteed.

tip

Generic collections nest freely — Dictionary<int, List<string>>, List<List<int>>, and so on all work.

Legacy collections

The classic System.Collections types are still available and behave the same as on desktop .NET. They store object, so values are boxed and must be cast on the way out.

ArrayList

ArrayList is a dynamic-size array. Unlike a fixed-size Array, it grows automatically as items are added. Use .Add to append, .RemoveAt(index) or .Remove(item) to remove, .Clear to empty.

using System;
using System.Collections;
using System.Diagnostics;

struct RmaRecord {
public DateTime date;
public string modelNumber;
public string fault;
}

var rmaList = new ArrayList();

rmaList.Add(new RmaRecord { date = new DateTime(2026, 2, 21), modelNumber = "XY23", fault = "No power" });
rmaList.Add(new RmaRecord { date = new DateTime(2026, 2, 20), modelNumber = "XY42", fault = "Blown fuse" });

Debug.WriteLine("Count: " + rmaList.Count);
Debug.WriteLine("Capacity: " + rmaList.Capacity);

foreach (RmaRecord r in rmaList) {
Debug.WriteLine(r.date.Year + "/" + r.date.Month + "/" + r.date.Day + " — " + r.modelNumber + " — " + r.fault);
}

Capacity vs CountCount is how many items are actually in the list; Capacity is the underlying allocation, which grows as items are added.

Hashtable

Hashtable stores key/value pairs and looks up values by key in roughly constant time. Keys must be unique (adding a duplicate throws). Values can repeat freely.

using System.Collections;
using System.Diagnostics;

var processorPin = new Hashtable();

processorPin.Add("LDR", "PE3");
processorPin.Add("APP", "PB7");
processorPin.Add("MOD", "PD7");
processorPin.Add("WKUP", "PA0");

// Indexer syntax adds a new key/value pair if the key isn't present yet.
processorPin["BTN4"] = "PD9";

// Lookup by key.
var ldrPin = processorPin["LDR"]; // "PE3"

// Enumerate as DictionaryEntry.
foreach (DictionaryEntry entry in processorPin) {
Debug.WriteLine(entry.Key + " = " + entry.Value);
}
note

Enumeration order is not deterministic — it depends on the hash function and the internal layout. Don't rely on the order matching the insertion order.

Stack

Stacks are LIFO (last in, first out): the last item pushed is the first one popped. Useful for undo histories, parser state, and depth-first traversal.

using System.Collections;
using System.Diagnostics;

var devices = new Stack();
devices.Push("SC20100S");
devices.Push("SC20260B");
devices.Push("SCM20260D");

Debug.WriteLine("Top: " + devices.Peek()); // SCM20260D — without removing.
Debug.WriteLine("Pop: " + devices.Pop()); // SCM20260D — removed.
Debug.WriteLine("Now top: " + devices.Peek()); // SC20260B.

Debug.WriteLine("Remaining (" + devices.Count + "):");
foreach (var item in devices)
Debug.WriteLine(" " + item);

Queue

Queues are FIFO (first in, first out): the order items were added is the order they come out. Useful for work backlogs, message buffers, and breadth-first traversal.

using System.Collections;
using System.Diagnostics;

var work = new Queue();
work.Enqueue("Task A");
work.Enqueue("Task B");
work.Enqueue("Task C");

Debug.WriteLine("Next: " + work.Peek()); // Task A — without removing.
Debug.WriteLine("Take: " + work.Dequeue()); // Task A — removed.
Debug.WriteLine("Now next: " + work.Peek()); // Task B.

Debug.WriteLine("Remaining (" + work.Count + "):");
foreach (var item in work)
Debug.WriteLine(" " + item);

API reference

NamespaceDescription
GHIElectronics.TinyCLR.CollectionsHashSet<T> / ISet<T>

List<T>, Dictionary<TKey, TValue>, and the non-generic System.Collections types ship in the core runtime (GHIElectronics.TinyCLR.Core).