# unmanaged 泛型约束

C# 的泛型的底层算是 C++ 模板和 Java 类型擦除的泛型的折中处理机制，它比 Java 的类型擦除要复杂，但比 C++ 模板要简单。C# 泛型是支持指针的，但指针并不安全，所以 C# 7 之前都不允许我们直接对泛型类型处理和操作指针。

C# 7 诞生了一种机制，允许我们通过 `unmanaged` 约束来保证一个类型可直接使用指针。

## Part 1 引例 <a href="#an-example" id="an-example"></a>

假设我们写一个对普通变量的交换函数。

```csharp
public static void Swap<T>(ref T left, ref T right) where T : struct
{
    var temp = left;
    left = right;
    right = temp;
}
```

这么做没有问题，但问题在于不够通用。如果结构体里包含引用类型字段的话，交换了也是没有完全交换成功的。所以，我们干脆限制成“非托管类型”，而不是 `struct` 约束，即值类型。

非托管类型是什么？非托管类型就是那些可以用指针处理的类型。只要类型本身只包含非 `object` 和 `string` 这两个引用类型以外的剩余的所有内置类型作为数据成员（字段或属性），那么这样的类型实例化后，赋值就会全数据成员挨个赋值，两个对象完全独立了。

这样的类型称为非托管类型。但是，我们知道我们无法使用泛型处理，因为这样的非托管类型无法使用任何之前学到过的泛型约束来完成。所以，`unmanaged` 约束就诞生了。

我们使用 `unmanaged` 约束约束类型即可。

```csharp
public static unsafe void Swap<T>(T* left, T* right) where T : unmanaged
{
    var temp = *left;
    *left = *right;
    *right = temp;
}
```

我们直接使用 `where T : unmanaged` 约束就可以对泛型类型使用指针了。这样的话，交换函数就不必使用 `ref` 了（当然用 `ref` 也可以），然后这里改成指针，C# 就允许了。

## Part 2 `unmanaged` 约束允许的操作 <a href="#allowed-operations-using-unmanaged-constraint" id="allowed-operations-using-unmanaged-constraint"></a>

`unmanaged` 约束允许如下两种操作：

* 指针使用（使用这个泛型类型作为基本类型，创建指针类型对象指向这样的数据类型）；
* `sizeof(T)` 表达式使用。

后面这个 `sizeof(T)` 我们说说是什么东西。C# 的引用类型在内存存储里是相当复杂的，除了从 C/C++ 沿用下来的内存对齐机制，还有一些别的规则，因此往往这样的大小我们是无法直接获取的。但是值类型的特例：非托管类型不同。非托管类型完全不需要 GC 就可以内存分配和释放，它的内存大小就没有那么多复杂的机制。C# 允许使用 `sizeof` 运算符计算一个非托管类型的内存大小，针对于内置类型来说，它们是常量；但对于其它自定义的非托管类型的话，就不是常量了。因此，它需要 `unmanaged` 的约束才可使用，然后底层就可以安全计算到正确的结果。

## Part 3 `sizeof(T)` 泛型用例 <a href="#generic-sizeof-expression" id="generic-sizeof-expression"></a>

考虑计算更快的 `Enum.HasFlag` 方法的 API。我们如果要自己写一个计算更快的、不需要反射处理的机制完成的方法的话，就需要指针来完成。恰好，`Enum` 是基于整数类型的；而 `Enum` 刚好也确实都是 `unmanaged` 类型的对象，因此可以直接对枚举类型的对象使用这种约束。

```csharp
public static unsafe bool HasFlag<T>(this T @this, T another) where T : unmanaged, Enum
{
    switch (sizeof(T))
    {
        case 1:
        case 2:
        case 4:
            int i = Unsafe.As<T, int>(ref other);
            return (Unsafe.As<T, int>(ref @this) & i) == i;
        case 8:
            long l = Unsafe.As<T, long>(ref other);
            return (Unsafe.As<T, long>(ref @this) & l) == l;
        default:
            throw new ArgumentException(
                "The parameter should be one of the values 1, 2, 4 or 8.",
                nameof(@this)
            );
    }
}
```

甚至你可以使用 `switch` 语句来完成：

```csharp
public static unsafe bool HasFlag<T>(this T @this, T another) where T : unmanaged, Enum =>
    sizeof(T) switch
    {
        1 or 2 or 4 when Unsafe.As<T, int>(ref other) is var i => (Unsafe.As<T, int>(ref @this) & i) == i,
        8 when Unsafe.As<T, long>(ref other) is var l => (Unsafe.As<T, long>(ref @this) & l) == l,
        _ => throw new ArgumentException(
            "The parameter should be one of the values 1, 2, 4 or 8.",
            nameof(@this)
        )
    };
```

很方便，对吧。只需要加一个 `unmanaged` 约束到 `Enum` 约束的后面就可以了。

## Part 4 为什么 `Enum` 约束的语义不直接包含 `unmanaged` 呢？ <a href="#why-enum-types-donot-auto-include-unmanaged-constraint" id="why-enum-types-donot-auto-include-unmanaged-constraint"></a>

问题问得好。既然所有的枚举类型都是整数类型互相转换，底层也是由整数类型来实现的，它们也都直接就是 `unmanaged` 约束，那么为什么我们还要自己写上 `unmanaged` 约束呢？

这是因为 C# 语法设计上，`Enum` 只是类型的一种约束，也就是说，`Enum` 约束只约束这个对象从 `Enum` 类型派生下来，而并不能自动包含 `unmanaged` 这样的语义，所以我们才需要自己加。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://csharp.kazusa.tech/csharp-7/unmanaged-type-parameter-constraint.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
