I don't have enough reputation for adding a comment, so I will write my comment as an answer:
I've tested the code proposed by @IS4 and his function says a string is not blittable, which is correct. However, when using the Mono backend in Unity, his implementation also says a struct with a string field is blittable (which is not correct).
I've also tested the Unity's UnsafeUtility.IsBlittable()
function and it returns the correct value for those structs, so if we want to implement an IsBlittable()
function which works correctly on Mono, I think we have no choice but using Reflection to ensure all the fields in the struct are blittable too.
I have tested this implementation in Unity 2017.4 and Unity 2018.4 using the Mono scripting backend and it seems to work correctly with all the types I've tried so far:
using System;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.InteropServices;
public static class BlittableHelper
{
#if UNITY_2018_1_OR_NEWER || UNITY_2019_1_OR_NEWER || UNITY_2020_1_OR_NEWER
// If we're using Unity, the simplest solution is using
// the built-in function
public static bool IsBlittableType(Type type)
{
return Unity.Collections.LowLevel.Unsafe.UnsafeUtility.IsBlittable(
type
);
}
#else
// NOTE: static properties are not taken into account when
// deciding whether a type is blittable, so we only need
// to check the instance fields and properties.
private static BindingFlags Flags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
public static bool IsBlittableType(Type type)
{
// According to the MSDN, one-dimensional arrays of blittable
// primitive types are blittable.
if (type.IsArray)
{
// NOTE: we need to check if elem.IsValueType because
// multi-dimensional (jagged) arrays are not blittable.
var elem = type.GetElementType();
return elem.IsValueType && IsBlittableType(elem);
}
// These are the cases which the MSDN states explicitly
// as blittable.
if
(
type.IsEnum
|| type == typeof(Byte)
|| type == typeof(SByte)
|| type == typeof(Int16)
|| type == typeof(UInt16)
|| type == typeof(Int32)
|| type == typeof(UInt32)
|| type == typeof(Int64)
|| type == typeof(UInt64)
|| type == typeof(IntPtr)
|| type == typeof(UIntPtr)
|| type == typeof(Single)
|| type == typeof(Double)
)
{
return true;
}
// These are the cases which the MSDN states explicitly
// as not blittable.
if
(
type.IsAbstract
|| type.IsAutoLayout
|| type.IsGenericType
|| type == typeof(Array)
|| type == typeof(Boolean)
|| type == typeof(Char)
//|| type == typeof(System.Class)
|| type == typeof(Object)
//|| type == typeof(System.Mdarray)
|| type == typeof(String)
|| type == typeof(ValueType)
|| type == typeof(Array)
//|| type == typeof(System.Szarray)
)
{
return false;
}
// If we've reached this point, we're dealing with a complex type
// which is potentially blittable.
try
{
// Non-blittable types are supposed to throw an exception,
// but that doesn't happen on Mono.
GCHandle.Alloc(
FormatterServices.GetUninitializedObject(type),
GCHandleType.Pinned
).Free();
// So we need to examine the instance properties and fields
// to check if the type contains any not blittable member.
foreach (var f in type.GetFields(Flags))
{
if (!IsBlittableTypeInStruct(f.FieldType))
{
return false;
}
}
foreach (var p in type.GetProperties(Flags))
{
if (!IsBlittableTypeInStruct(p.PropertyType))
{
return false;
}
}
return true;
}
catch
{
return false;
}
}
private static bool IsBlittableTypeInStruct(Type type)
{
if (type.IsArray)
{
// NOTE: we need to check if elem.IsValueType because
// multi-dimensional (jagged) arrays are not blittable.
var elem = type.GetElementType();
if (!elem.IsValueType || !IsBlittableTypeInStruct(elem))
{
return false;
}
// According to the MSDN, a type that contains a variable array
// of blittable types is not itself blittable. In other words:
// the array of blittable types must have a fixed size.
var property = type.GetProperty("IsFixedSize", Flags);
if (property == null || !(bool)property.GetValue(type))
{
return false;
}
}
else if (!type.IsValueType || !IsBlittableType(type))
{
// A type can be blittable only if all its instance fields and
// properties are also blittable.
return false;
}
return true;
}
#endif
}
// This class is used as a caching mechanism to improve performance.
public static class BlittableHelper<T>
{
public static readonly bool IsBlittable;
static BlittableHelper()
{
IsBlittable = BlittableHelper.IsBlittableType(typeof(T));
}
}