我没有足够的声誉来添加评论,所以我会写我的评论作为答案:
我已经测试了@IS4 提出的代码,他的函数说字符串不是 blittable,这是正确的。但是,当在 Unity 中使用 Mono 后端时,他的实现还说带有字符串字段的结构是 blittable(这是不正确的)。
我还测试了 Unity 的UnsafeUtility.IsBlittable()
函数,它为这些结构返回了正确的值,所以如果我们想实现一个IsBlittable()
在 Mono 上正常工作的函数,我认为我们别无选择,只能使用反射来确保结构中的所有字段都是也可以blittable。
我已经使用 Mono 脚本后端在 Unity 2017.4 和 Unity 2018.4 中测试了这个实现,它似乎与我迄今为止尝试过的所有类型都能正常工作:
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));
}
}