Version: Unity 7.0 Alpha (7000.0)
Language : English
Using unmanaged collection types
Collection types reference

Introduction to unmanaged collections

The Unity.Collections namespace provides unmanaged C# containers that you can use in jobs and Burst-compiled code. The collections in this namespace fall into the following categories:

  • Collection types with safety checks to ensure they’re used in a thread-safe way and properly disposed of. These types are in the Unity.Collections namespace and their names start with Native.
  • Collection types without safety checks. These types are in the Unity.Collections.LowLevel.Unsafe namespace and their names start with Unsafe.

The remaining collection types that don’t fit into these categories aren’t allocated and don’t contain any pointers. These types only contain small amounts of data, and their disposal and thread safety aren’t a concern.

Safety handles and checks

Native collection types carry Unity’s AtomicSafetyHandle, which performs validation for access, disposal, and job dependencies. The Unsafe equivalents don’t carry these safety handles. These safety handles are separate from index bounds checking, and several Unsafe types, such as UnsafeList and UnsafePtrList, bounds-check their indexers via CollectionHelper.CheckIndexInRange. This works in the same way as the equivalent conditional checks in Native types.

Checks are always enabled in the Editor, and in Player builds with the Checked managed code variant. The only way to make these checks fully zero-cost is to run in a Player build that doesn’t include safety checks.

It’s important to understand that disabling safety checks in the Editor doesn’t compile out these checks:

  • The Burst menu option to disable safety checks has a specific and limited effect: when a subset of checks are called from Burst-compiled code, they’re compiled out. If Burst is disabled, or if a method runs outside of Burst for any other reason, this option has no effect on that code.
  • Disabling the jobs debugger disables a subset of jobs debugger threading checks, but doesn’t reduce the overall cost of checks to zero.

Neither of these options reduces the cost of checks overall to zero.

Native versus Unsafe collections

It’s best practice to use Native collections over the Unsafe equivalents, because the safety handles catch access, disposal, and job-dependency errors. However, it’s sometimes necessary to use the Unsafe equivalents, because Native collection types can’t contain other Native collections. This limitation comes from how Unity implements the Native safety handles. For example, if you want a list of lists, you can use either NativeList<UnsafeList<T>> or UnsafeList<UnsafeList<T>>, but you can’t use NativeList<NativeList<T>>.

Most Native collections are implemented as wrappers of their Unsafe counterparts. For example, NativeList is made up of an UnsafeList plus the handles that the safety checks use. Because of this wrapping, a Native type is not free even when checks are compiled out: the internal Unsafe collection introduces an extra pointer indirection, which adds cost by itself even in unchecked Player builds.

Several Native types have Unsafe equivalents, for example, NativeList has UnsafeList, and NativeHashMap has UnsafeHashMap.

The following table summarizes the comparative safety features of Native and Unsafe collection types:

Behavior Native Unsafe
Job safety checks Yes No
Index bound checks Yes Partially (when safety checks are enabled)
Containing behavior Can only contain Unsafe collections Can contain either

Passing collections by value

The way changes are reflected when you pass a collection by value differs between collection types:

  • If you pass a NativeList by value and either write into the data or resize the list, the changes are reflected in the caller’s copy of the list.
  • If you pass an UnsafeList by value and write into the data, those writes are reflected in the caller’s version. However, if you resize the list, that change is not reflected in the caller’s copy, and moreover all subsequent writes are not reflected either.
  • If you pass a FixedList by value, nothing is reflected in the caller’s copy, because the entire contents are copied when you pass it.

Additional resources

Using unmanaged collection types
Collection types reference