Skip to content

C# collections

The .NET base class library contains multiple collection types that can be used to store and manipulate data. Godot also provide some collection types that are tightly integrated with the rest of the engine.

Choose a collection

The main difference between the .NET collections and the Godot collections is that the .NET collections are implemented in C# while the Godot collections are implemented in C++ and the Godot C# API is a wrapper over it, this is an important distinction since it means every operation on a Godot collection requires marshaling which can be expensive especially inside a loop.

Due to the performance implications, using Godot collections is only recommended when absolutely necessary (such as interacting with the Godot API). Godot only understands its own collection types, so it's required to use them when talking to the engine.

If you have a collection of elements that don't need to be passed to a Godot API, using a .NET collection would be more performant.

TIP

It's also possible to convert between .NET collections and Godot collections. The Godot collections contain constructors from generic .NET collection interfaces that copy their elements, and the Godot collections can be used with the LINQToList, ToArray and ToDictionary methods. But keep in mind this conversion requires marshaling every element in the collection and copies it to a new collection so it can be expensive.

Despite this, the Godot collections are optimized to try and avoid unnecessary marshaling, so methods like Sort or Reverse are implemented with a single interop call and don't need to marshal every element. Keep an eye out for generic APIs that take collection interfaces like LINQ because every method requires iterating the collection and, therefore, marshaling every element. Prefer using the instance methods of the Godot collections when possible.

To choose which collection type to use for each situation, consider the following questions:

  • Does your collection need to interact with the Godot engine? (e.g.: the type of an exported property, calling a Godot method).

  • Do you need a Godot collection that represents a list or sequential set of data?

    • Godot Array are similar to the C# collection List<T>.
    • Godot PackedArray are more memory-efficient arrays, in C# use one of the supported System.Array types.
  • Do you need a Godot collection that maps a set of keys to a set of values?

    • Godot Dictionary store pairs of keys and values and allow easy access to the values by their associated key.

Godot collections

PackedArray

Godot packed arrays are implemented as an array of a specific type, allowing it to be more tightly packed as each element has the size of the specific type, not Variant.

In C#, packed arrays are replaced by System.Array:

GDScriptC#
PackedByteArraybyte[]
PackedInt32Arrayint[]
PackedInt64Arraylong[]
PackedFloat32Arrayfloat[]
PackedFloat64Arraydouble[]
PackedStringArraystring[]
PackedVector2ArrayVector2[]
PackedVector3ArrayVector3[]
PackedColorArrayColor[]

Other C# arrays are not supported by the Godot C# API since a packed array equivalent does not exist. See the list of Variant-compatible types.

Array

Godot arrays are implemented as an array of Variant and can contain several elements of any type. In C#, the equivalent type is Godot.Collections.Array.

The generic Godot.Collections.Array<T> type allows restricting the element type to a Variant-compatible types.

An untyped Godot.Collections.Array can be converted to a typed array using the Godot.Collections.Array<T>(Godot.Collections.Array) constructor.

INFO

Despite the name, Godot arrays are more similar to the C# collection List<T> than System.Array. Their size is not fixed and can grow or shrink as elements are added/removed from the collection.

List of Godot's Array methods and their equivalent in C#:

GDScriptC#
allSystem.Linq.Enumerable.All
anySystem.Linq.Enumerable.Any
appendAdd
append_arrayAddRange
assignClear and AddRange
backArray[^1] or System.Linq.Enumerable.Last or System.Linq.Enumerable.LastOrDefault
bsearchBinarySearch
bsearch_customN/A
clearClear
countSystem.Linq.Enumerable.Count
duplicateDuplicate
eraseRemove
fillFill
filterUse System.Linq.Enumerable.Where
findIndexOf
frontArray[0] or System.Linq.Enumerable.First or System.Linq.Enumerable.FirstOrDefault
get_typed_builtinN/A
get_typed_class_nameN/A
get_typed_scriptN/A
hasContains
hashGD.Hash
insertInsert
is_emptyUse Count == 0
is_read_onlyIsReadOnly
is_same_typedN/A
is_typedN/A
make_read_onlyMakeReadOnly
mapSystem.Linq.Enumerable.Select
maxMax
minMin
pick_randomPickRandom (Consider using System.Random)
pop_atArray[i] with RemoveAt(i)
pop_backArray[^1] with RemoveAt(Count - 1)
pop_frontArray[0] with RemoveAt(0)
push_backInsert(Count, item)
push_frontInsert(0, item)
reduceSystem.Linq.Enumerable.Aggregate
remove_atRemoveAt
resizeResize
reverseReverse
rfindLastIndexOf
shuffleShuffle
sizeCount
sliceSlice
sortSort
sort_customSystem.Linq.Enumerable.OrderBy
operator !=!RecursiveEqual
operator +operator +
operator <N/A
operator <=N/A
operator ==RecursiveEqual
operator >N/A
operator >=N/A
operator []Array[int] indexer

Dictionary

Godot dictionaries are implemented as a dictionary with Variant keys and values. In C#, the equivalent type is Godot.Collections.Dictionary.

The generic Godot.Collections.Dictionary<TKey, TValue> type allows restricting the key and value types to a Variant-compatible types.

An untyped Godot.Collections.Dictionary can be converted to a typed dictionary using the Godot.Collections.Dictionary<TKey, TValue>(Godot.Collections.Dictionary) constructor.

TIP

If you need a dictionary where the key is typed but not the value, use Variant as the TValue generic parameter of the typed dictionary.

csharp
// The keys must be string, but the values can be any Variant-compatible type.
var dictionary = new Godot.Collections.Dictionary<string, Variant>();

List of Godot's Dictionary methods and their equivalent in C#:

GDScriptC#
clearClear
duplicateDuplicate
eraseRemove
find_keyN/A
getDictionary[Variant] indexer or TryGetValue
hasContainsKey
has_allN/A
hashGD.Hash
is_emptyUse Count == 0
is_read_onlyIsReadOnly
keysKeys
make_read_onlyMakeReadOnly
mergeMerge
sizeCount
valuesValues
operator !=!RecursiveEqual
operator ==RecursiveEqual
operator []Dictionary[Variant] indexer, Add or TryGetValue