如何检查类型T
是否符合unmanaged
类型约束,以便它可以在这样的上下文中使用:class Foo<T> where T : unmanaged
?我的第一个想法是typeof(T).IsUnmanaged
或类似的东西,但这不是Type
类的属性/字段
根据unmanaged
约束文件:
unmanaged
类型不是引用类型,并且在任何嵌套级别都不包含引用类型字段
在C#语言的设计文档中也提到了非托管类型约束:
为了满足此约束,类型必须是结构,并且该类型的所有字段必须属于以下类别之一:
-
具有类型
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、float
、double
、decimal
、bool
、IntPtr
或UIntPtr
-
可以是任何
enum
类型 - 是指针类型
-
是满足
unmanaged
约束的用户定义结构
注意事项
通常调用MakeGenericType
是验证CRL强制执行的泛型类型约束的最可靠的解决方案。通常,尝试自己实现验证不是一个好主意,因为可能有很多规则需要考虑,而且总是有可能错过其中的一些规则。但请注意,至少在编写这个答案时,它对unmanaged
约束不起作用。
.NET Core有一个RuntimeHelpers.IsReferenceOrContainsReferences
,但在编写这个答案时,.NET Framework没有这样的功能。我应该指出的是,即使使用IsReferenceOrContainsReferences
也不完全可靠。
例如,请参阅我在这里发布的关于两个结构的问题,这两个结构没有任何引用类型,但其中一个被评估为托管,另一个未被管理(可能是编译器错误)。
无论如何,现在根据您的偏好和要求,使用以下解决方案之一来检测哪种类型可以满足unmanaged
泛型类型约束。
选项1-使用MakeGenericType
作为一个选项,要检查类型是否满足unmanaged
约束,可以使用以下IsUnmanaged
扩展方法"。
C#7.3 :它应该更可靠,但我应该说,它不是。似乎对于
unmanaged
约束,CLR不尊重约束,它只是C#编译器的一个特性。所以至少对于现在,我建议使用第二个选项
C#8.0 :在C#8.0中按预期工作
using System;
using System.Reflection;
public static class UnmanagedTypeExtensions
{
class U<T> where T : unmanaged { }
public static bool IsUnManaged(this Type t)
{
try { typeof(U<>).MakeGenericType(t); return true; }
catch (Exception){ return false; }
}
}
选项2-编写自己的方法来检查记录的规则
作为另一种选择,您可以编写方法来检查unmanaged
约束的文档规则。以下代码有更多的规则,而不是其他答案,可以处理类似int?
或(int,int)
:的情况
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class UnmanagedTypeExtensions
{
private static Dictionary<Type, bool> cachedTypes =
new Dictionary<Type, bool>();
public static bool IsUnManaged(this Type t)
{
var result = false;
if (cachedTypes.ContainsKey(t))
return cachedTypes[t];
else if (t.IsPrimitive || t.IsPointer || t.IsEnum)
result = true;
else if (t.IsGenericType || !t.IsValueType)
result = false;
else
result = t.GetFields(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance)
.All(x => x.FieldType.IsUnManaged());
cachedTypes.Add(t, result);
return result;
}
}
更多信息
您可能会发现以下链接很有用:
- 文档-非管理约束
- GitHub-C#7.3语言设计文档-非托管类型约束
- Sergey Teplyakov关于剖析C#7.3中新的通用约束的博客文章
- Maarten Balliauw关于Unmanaged、delegate和enum类型约束的博客文章
- GitHub问题-请澄清非托管泛型约束的实现细节
- GitHub-提案:未管理的构造类型#1504
我不确定类似的东西是否已经存在,但您可以实现自己的扩展方法,类似于:
public static bool IsUnmanaged(this Type type)
{
// primitive, pointer or enum -> true
if (type.IsPrimitive || type.IsPointer || type.IsEnum)
return true;
// not a struct -> false
if (!type.IsValueType)
return false;
// otherwise check recursively
return type
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.All(f => IsUnmanaged(f.FieldType));
}
(更新)为了完整性,由于具有许多嵌套成员的结构的递归速度较慢,因此可以通过缓存结果来加快函数的速度:
private static readonly ConcurrentDictionary<Type, bool> _memoized =
new ConcurrentDictionary<Type, bool>();
public static bool IsUnmanaged(this Type type)
{
bool answer;
// check if we already know the answer
if (!_memoized.TryGetValue(type, out answer))
{
if (!type.IsValueType)
{
// not a struct -> false
answer = false;
}
else if (type.IsPrimitive || type.IsPointer || type.IsEnum)
{
// primitive, pointer or enum -> true
answer = true;
}
else
{
// otherwise check recursively
answer = type
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.All(f => IsUnmanaged(f.FieldType));
}
_memoized[type] = answer;
}
return answer;
}