You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为ref struct实现传统TryGet函数以避免重复查找?

如何为ref struct编写传统的TryGet函数?

我目前有一组配套函数:

bool HasX(int key) { ... }
ref S GetX(int key) { ... }  // S 是 struct

它们能正常工作,但如果要先检查存在性再调用获取函数,就得两次计算数组偏移并检查值。这正是Dictionary<TK, TV>.TryGetValue(TK key, out TV value)这类函数存在的意义——只需要一次查找即可完成存在性校验和值获取。

我尝试过将二者合并为一个函数的几种方案,但都存在问题:

// 问题:ref S的语义不符合需求,无法将其设置为引用结构句柄
bool TryGetX(int key, ref S value)

// 问题:out S会对struct进行值拷贝,失去了ref语义的优势
bool TryGetX(int key, out S value)

// 重复方案,同上问题
bool TryGetX(int key, ref S value)

// 问题:返回的是结构体拷贝而非引用,不符合需求
S? TryGetX(int key)

// 问题:等价于ref Nullable<S>,而非Nullable<ref S>,不适用
ref S? TryGetX(int key)

// 可行但不符合惯用写法:无法像`if(dict.TryGetValue(key, out var value))`那样简洁地作为条件使用
ref S TryGetX(int key, out bool found)

// 目前最简洁但仍不符合惯用写法:失败时返回Unsafe.NullRef<S>,需要额外判空
ref S TryGetX(int key)

可行解决方案

针对ref struct的场景,有两种兼顾性能和惯用性的实现方式:

方案1:调整返回顺序,配合模式匹配优化可读性

虽然与标准TryGetValue的参数顺序相反,但可以通过模式匹配让代码接近惯用风格:

ref S TryGetX(int key, out bool exists)
{
    // 执行一次查找逻辑:计算偏移、校验存在性
    if (/* 找到对应项 */)
    {
        exists = true;
        return ref 目标结构体引用;
    }
    exists = false;
    return ref Unsafe.NullRef<S>();
}

使用示例:

if (TryGetX(key, out var exists) is var valueRef && exists)
{
    // 直接操作valueRef,无结构体拷贝
    valueRef.SomeProperty = newValue;
}

方案2:自定义可空引用结果结构体

如果想要更贴近标准TryGetValue的语法,可以定义一个包含引用和存在标记的包装结构体:

readonly struct RefResult<T> where T : struct
{
    public readonly ref T Value;
    public readonly bool Exists;

    public RefResult(ref T value, bool exists)
    {
        Value = ref value;
        Exists = exists;
    }

    // 隐式转换为bool,支持直接作为条件判断
    public static implicit operator bool(RefResult<T> result) => result.Exists;
}

实现对应的TryGet函数:

RefResult<S> TryGetX(int key)
{
    // 一次查找逻辑
    if (/* 找到对应项 */)
    {
        return new RefResult<S>(ref 目标结构体引用, true);
    }
    return new RefResult<S>(ref Unsafe.NullRef<S>(), false);
}

使用示例(和标准写法高度一致):

if (TryGetX(key) is var result && result)
{
    // 操作result.Value,无拷贝
    result.Value.SomeProperty = newValue;
}

// 或者简化写法
var result = TryGetX(key);
if (result)
{
    // 使用result.Value
}

关键注意点

  • 避免使用out S的方案,因为会产生struct拷贝,完全失去ref语义的性能优势
  • 返回Unsafe.NullRef<S>的方案必须严格检查空引用,否则会导致内存访问错误
  • 自定义RefResult<T>时建议设为readonly struct,避免不必要的结构体拷贝

内容的提问来源于stack exchange,提问作者Blindy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 05:35:28