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

标签 c# generics reflection types c#-7.3

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

最佳答案

根据 unmanaged约束文档:

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

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

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

  • 具有类型 sbytebyteshortushortint, uint, long, ulong, char, float, doubledecimalboolIntPtrUIntPtr<
  • 可以是任何 enum 类型。
  • 是指针类型。
  • 是满足 unmanaged 约束的用户定义结构。

注意事项

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

.NET Core 有一个 RuntimeHelpers.IsReferenceOrContainsReferences但是在写这个答案的时候,.NET Framework 没有这样的功能。我应该提一下,即使使用 IsReferenceOrContainsReferences 也不能完全可靠地完成这项任务。

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

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

选项 1 - 使用 MakeGenericType

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

C# 7.3: It is supposed to be more reliable, but I should say, it's not. It seems for unmanaged constraint, CLR is not respecting the constraint and it's just a C# compiler feature. So at least for now, I recommend using the second option.

C# 8.0: Works as expected in 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;
    }
}

更多信息

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

关于c# - 如何检查类型是否符合 C# 中的非托管约束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53968920/

相关文章:

c# - 对于使用 .NET Core Version 2+ 创建的软件,需要转到 “live” ,是否有任何理由在目标 “live” 操作系统上安装 .NET Core 运行时?

c# - 将字符串添加到逐字字符串文字

.net - 您可以使用反射来查找当前正在执行的方法的名称吗?

c# - 如何调用返回接口(interface)的方法

c# - IEnumerable 到 T[] 数组

scala - 从方法符号和主体创建方法定义树

c# - 如何在实例化大量控件时不阻塞 UI

c# - 从 ASP.NET 日历控件的单元格触发事件

Java 泛型通配符问题 : List<? extends A>

c# - List<T>.ForEach 带索引