如何检查类型是否符合C#中的非托管约束



如何检查类型T是否符合unmanaged类型约束,以便它可以在这样的上下文中使用:class Foo<T> where T : unmanaged?我的第一个想法是typeof(T).IsUnmanaged或类似的东西,但这不是Type类的属性/字段

根据unmanaged约束文件:

unmanaged类型不是引用类型,并且在任何嵌套级别都不包含引用类型字段

在C#语言的设计文档中也提到了非托管类型约束:

为了满足此约束,类型必须是结构,并且该类型的所有字段必须属于以下类别之一:

  • 具有类型sbytebyteshortushortintuintlongulongcharfloatdoubledecimalboolIntPtrUIntPtr
  • 可以是任何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;
}

相关内容

  • 没有找到相关文章

最新更新