C# 泛型约束之递归约束
public interface IContactManifold<TManifold> where TManifold : struct, IContactManifold<TManifold>
这个泛型约束 where TManifold : struct, IContactManifold<TManifold> 是一个 递归泛型约束(Recursive Generic Constraint),常见于一些高级设计模式(如 Curiously Recurring Template Pattern, CRTP)。我们来逐步解析它的含义:
1. 分解约束条件
public interface IContactManifold<TManifold>
where TManifold : struct, IContactManifold<TManifold>
TManifold是泛型参数。where TManifold : struct:- 表示
TManifold必须是 值类型(如struct或enum),不能是引用类型(如class)。
- 表示
where TManifold : IContactManifold<TManifold>:- 表示
TManifold必须实现IContactManifold<TManifold>接口。 - 这是一个 递归约束,因为
IContactManifold的泛型参数是TManifold本身。
- 表示
2. 设计意图
这种约束通常用于:
- 强制实现类自身作为泛型参数,确保类型安全。
- 允许接口方法操作具体的实现类型(而不是基类或接口类型)。
- 常见于数学库、物理引擎(如碰撞检测的
ContactManifold结构)。
示例
// 具体实现
public struct SphereContactManifold : IContactManifold<SphereContactManifold>
{
// 必须实现接口方法,且方法的参数/返回值是 SphereContactManifold 类型
public void Process(SphereContactManifold other) { ... }
}
// 错误示例:编译失败,因为 int 不满足 IContactManifold<int>
public struct InvalidManifold : IContactManifold<int> { ... }
3. 为什么用递归泛型?
- 类型安全:确保接口方法操作的是具体的实现类型(如
SphereContactManifold),而不是基接口。 - 避免装箱:
struct约束保证值类型语义,防止性能损失。 - 模式复用:类似 C++ 的 CRTP,用于静态多态(编译时多态)。
4. 对比普通泛型接口
普通泛型接口:
public interface IContactManifold<T> where T : struct { ... }
- 允许
T是任意值类型(如int、Vector3),不一定与接口相关。
递归泛型接口:
public interface IContactManifold<T> where T : struct, IContactManifold<T> { ... }
- 强制
T必须实现IContactManifold<T>,形成类型闭环。
5. 实际应用场景
假设你在编写一个物理引擎的碰撞检测系统:
public interface IContactManifold<TManifold>
where TManifold : struct, IContactManifold<TManifold>
{
bool CollideWith(TManifold other);
}
// 具体碰撞类型
public struct SphereManifold : IContactManifold<SphereManifold>
{
public bool CollideWith(SphereManifold other) { ... }
}
public struct BoxManifold : IContactManifold<BoxManifold>
{
public bool CollideWith(BoxManifold other) { ... }
}
- 这样设计能确保
SphereManifold只能与SphereManifold碰撞,而不是与BoxManifold混用。
总结
where TManifold : struct→TManifold必须是值类型。where TManifold : IContactManifold<TManifold>→TManifold必须实现当前接口,且泛型参数是自身。- 这种模式用于 类型安全的自引用泛型设计,常见于数学/物理库。
这个声明是一个 静态抽象接口方法(C# 11+ 引入的特性),结合了 ref 返回和泛型约束。我们来逐步解析它的含义:
方法签名分解
static abstract ref Vector3 GetOffsetReference(ref TManifold manifold, int contactIndex);
-
static abstract- 这是 C# 11 引入的 静态抽象接口方法(Static Abstract Members in Interfaces)。
- 允许接口定义静态方法,实现该接口的类/结构体必须提供对应的静态实现。
- 主要用于泛型数学和模式匹配场景。
-
ref Vector3- 表示方法返回一个
Vector3的引用(而不是值拷贝)。 - 调用方可以直接修改原始数据(避免复制开销,提升性能)。
- 表示方法返回一个
-
ref TManifold manifold- 参数通过引用传递(
ref),避免结构体的拷贝开销。 TManifold是泛型类型,通常受接口自身的递归泛型约束(如where TManifold : IContactManifold<TManifold>)。
- 参数通过引用传递(
-
int contactIndex- 表示要获取的接触点索引(例如碰撞检测中的第
i个接触点)。
- 表示要获取的接触点索引(例如碰撞检测中的第
设计意图
这种设计通常用于:
- 高性能场景
ref传递和返回避免值类型的拷贝(如物理引擎中的Vector3坐标)。
- 泛型数学/物理计算
- 结合
static abstract实现编译时多态(类似 C++ 的 CRTP)。
- 结合
- 直接操作内存数据
- 返回引用允许外部代码直接修改原始数据(例如更新碰撞点的偏移量)。
示例实现
假设有一个物理引擎的 ContactManifold 接口:
public interface IContactManifold<TManifold>
where TManifold : struct, IContactManifold<TManifold>
{
// 静态抽象方法:获取第 contactIndex 个接触点的偏移量(引用返回)
static abstract ref Vector3 GetOffsetReference(ref TManifold manifold, int contactIndex);
}
// 具体实现(例如球体碰撞)
public struct SphereContactManifold : IContactManifold<SphereContactManifold>
{
private Vector3[] _contactOffsets; // 存储接触点偏移量
// 实现静态方法
public static ref Vector3 GetOffsetReference(ref SphereContactManifold manifold, int contactIndex)
{
// 返回指定索引的偏移量的引用
return ref manifold._contactOffsets[contactIndex];
}
}
使用场景
// 修改碰撞点的偏移量(直接操作原始数据)
SphereContactManifold manifold = new();
ref Vector3 offset = ref SphereContactManifold.GetOffsetReference(ref manifold, 0);
offset.X += 1.0f; // 直接修改 manifold 内部数据
关键点总结
| 特性 | 作用 |
|---|---|
static abstract |
强制实现类提供静态方法,支持泛型编译时多态。 |
ref Vector3 |
返回 Vector3 的引用(避免拷贝,允许直接修改原始数据)。 |
ref TManifold |
泛型参数按引用传递(避免结构体拷贝)。 |
| 递归泛型约束 | 确保 TManifold 是实现了当前接口的具体类型(如 SphereContactManifold)。 |
为什么需要这种设计?
- 性能优化
ref返回/传递避免值类型的内存复制(对Vector3等小型结构体很重要)。
- 类型安全
- 递归泛型约束确保
GetOffsetReference只能操作具体的TManifold类型(如SphereContactManifold)。
- 递归泛型约束确保
- 扩展性
- 不同碰撞类型(球体、盒子)可以自定义
GetOffsetReference的实现逻辑。
- 不同碰撞类型(球体、盒子)可以自定义
对比普通接口方法
-
普通接口方法:
Vector3 GetOffset(int contactIndex); // 返回值拷贝- 每次调用会产生
Vector3的拷贝,无法直接修改原始数据。
- 每次调用会产生
-
ref返回方法:ref Vector3 GetOffsetReference(int contactIndex); // 返回引用- 零拷贝,可直接读写原始数据,适合高性能场景。
适用场景
- 物理引擎(如碰撞检测、接触点计算)。
- 游戏开发(如 Unity DOTS 架构中的 ECS 数据操作)。
- 数学库(如矩阵/向量计算)。
#####
愿你一寸一寸地攻城略地,一点一点地焕然一新
#####

浙公网安备 33010602011771号