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

时间:2018-12-29 11:00:40

标签: c# generics reflection types c#-7.3

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

2 个答案:

答案 0 :(得分:4)

根据unmanaged约束文档:

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

C#语言设计文档中也提到了unmanaged type constraint

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

  • 具有类型sbytebyteshortushortintuintlongulongcharfloatdoubledecimalboolIntPtrUIntPtr。 / em>
  • 可以是任何enum类型。
  • 成为指针类型。
  • 成为用户定义的结构,该结构满足unmanaged约束。

注意事项

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

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

例如,关于the issue which I posted here的两个结构没有任何引用类型,但其中一个被评估为托管,其中一个为非托管(可能是编译器错误)。

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

选项1-使用MakeGenericType

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

  

注意: 本来应该更可靠,但我应该说不是。对于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; }
    }
}

选项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;
    }
}

更多信息

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

答案 1 :(得分:3)

我不确定是否已经存在类似的东西,但是您可以实现类似于以下内容的扩展方法:

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;
}