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

Joh*_*ohn 10 c# generics reflection types c#-7.3

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

Rez*_*aei 7

根据unmanaged约束文件:

一种unmanaged类型是一个类型不引用类型和在任何嵌套级别不包含引用类型字段。

C#语言设计文档中也提到了有关非托管类型约束的内容

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

  • 有型sbytebyteshortushortintuintlongulongcharfloatdoubledecimalboolIntPtrUIntPtr
  • 可以是任何enum类型。
  • 是指针类型。
  • 是满足unmanaged约束的用户定义结构。

注意事项

通常,调用MakeGenericType是验证CRL强制执行的通用类型约束的最可靠解决方案。通常,尝试自己实施验证不是一个好主意,因为您可能需要考虑很多规则,并且总是有可能遗漏其中一些规则。但是请注意,至少在编写此答案时,它不能很好地进行unmanaged约束。

.NET Core有一个,RuntimeHelpers.IsReferenceOrContainsReferences但是在编写此答案时,.NET Framework没有此功能。我应该提到,即使使用IsReferenceOrContainsReferences该工具也不完全可靠。

例如,请参见我在此处发布的关于两个结构的问题,该结构没有任何引用类型,但其中一个被评估为托管,其中一个被托管(可能是编译器错误)。

无论如何,现在根据您的喜好和要求,使用以下解决方案之一来检测哪种类型可以满足unmanaged通用类型约束。

选项1-使用MakeGenericType

作为一种选择,要检查类型是否可以满足unmanaged约束,可以使用以下IsUnmanaged扩展方法。

注意:应该是更可靠的sta,但是我应该说不是。似乎出于unmanaged约束,CLR不遵守约束,而只是C#编译器功能。因此,至少现在,我建议使用第二个选项。

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

选项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;
    }
}
Run Code Online (Sandbox Code Playgroud)

更多信息

您可能会发现以下链接有用: