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

C#从List或Dictionary获取结构体ref返回的可行替代方案有哪些?

解决方案总览

1. 无需包装类的原生方案(推荐)

针对List<T>

使用System.Runtime.InteropServices.CollectionsMarshal.AsSpan()方法将List转换为Span<T>,即可通过ref访问内部元素,全程无拷贝:

List<MyStruct> structList = new List<MyStruct>();
structList.Add(new MyStruct { foo = 42 });

// 直接获取索引0元素的引用
ref MyStruct itemRef = ref CollectionsMarshal.AsSpan(structList)[0];
itemRef.foo = 43; // 直接修改List内的原始结构体,无拷贝

针对Dictionary<TKey, TValue>

使用CollectionsMarshal.GetValueRefOrNullRef或GetValueRefOrAddDefault方法直接获取值的引用:

Dictionary<DateTime, MyStruct> timeStructMap = new Dictionary<DateTime, MyStruct>();
timeStructMap.Add(DateTime.Today, new MyStruct { foo = 42 });

// 获取对应键的值引用,键不存在时返回空引用
ref MyStruct msRef = ref CollectionsMarshal.GetValueRefOrNullRef(timeStructMap, DateTime.Today);
if (!System.Runtime.CompilerServices.Unsafe.IsNullRef(ref msRef))
{
    msRef.foo = 43; // 直接修改字典内的原始结构体,无拷贝
}

2. 通用泛型包装类方案

如果你确实需要跨作用域持有结构体引用、或者需要多集合共享同一个结构体实例,可以写一个通用的泛型包装类,无需为每个结构体单独定义:

public class ValueWrapper<T> where T : struct
{
    public T Value;
    public ValueWrapper(T value) => Value = value;
}

使用方式和你现有的自定义包装类一致:

Dictionary<DateTime, ValueWrapper<MyStruct>> timeWrapMap = new();
timeWrapMap.Add(DateTime.Today, new ValueWrapper<MyStruct>(new MyStruct { foo = 42 }));

var wrap = timeWrapMap[DateTime.Today];
wrap.Value.foo = 43; // 修改生效

注意:.NET 官方并没有提供内置的通用值类型包装类,上述通用实现足以覆盖绝大多数包装场景。

注意事项

  • 使用CollectionsMarshal系列API拿到结构体引用后,不要对原集合执行添加/删除/扩容操作,否则会导致引用失效,产生未定义行为
  • 如果你的结构体大小小于16字节,直接拷贝替换的开销几乎可以忽略,优先用更安全的全值替换写法:timeStructMap[DateTime.Today] = new MyStruct { foo = 43 };
  • 可变结构体本身确实存在易误用的问题,如果没有强制性能要求,优先考虑将结构体设计为不可变,从根源避免意外拷贝修改失效的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 17:06:05