存储对结构数组的 C# 引用并检索它 - 可以不复制吗?

Storing a C# reference to an array of structs and retrieving it - possible without copying?

更新:下一个版本的 C# 有一个正在考虑中的功能,可以直接回答这个问题。 c.f。下面的答案。


要求:

  1. 应用程序数据存储在结构数组中。应用程序中的每种数据类型都有一个 AoS(例如,一个用于 MyStruct1,另一个用于 MyStruct2,等等)
  2. 结构是在运行时创建的;我们在应用程序中编写的代码越多,就会越多。
  3. 我需要一个 class 来保存对所有 AoS 的引用,并允许我在这些 AoS 中设置和获取单个结构
  4. AoS 往往很大(每个数组有 1,000 个结构);复制那些 AoS 将彻底失败——永远不应该复制它们! (他们永远不需要!)

我有可以编译和运行的代码,并且可以正常工作...但是 C# 是否会在我每次访问它们时默默地复制 AoS? (完整来源见下文)

public Dictionary<System.Type, System.Array> structArraysByType;

public void registerStruct<T>()
{
    System.Type newType = typeof(T);
    if( ! structArraysByType.ContainsKey(newType ) )
    {
        structArraysByType.Add(newType, new T[1000] ); // allowing up to 1k
    }   
}

public T get<T>( int index )
{
    return ((T[])structArraysByType[typeof(T)])[index];
}

public void set<T>( int index, T newValue )
{
    ((T[])structArraysByType[typeof(T)])[index] = newValue;
}

备注:

据我所知,你正在做的应该工作正常,但是是的,当你调用 Get 时,它会 return 一个结构 T 实例的副本,并且当您调用 Set 时,使用基于堆栈的实例执行替换。除非你的结构很大,否则这应该不是问题。

如果它们很大而你想要

  • 读取数组中结构实例之一的(某些)属性,而不创建它的副本。
  • 更新它的一些字段(你的结构不应该是不可变的,这通常是个坏主意,但这样做有充分的理由)

然后您可以将以下内容添加到您的 class:

public delegate void Accessor<T>(ref T item) where T : struct;
public delegate TResult Projector<T, TResult>(ref T item) where T : struct;

public void Access<T>(int index, Accessor<T> accessor)
{
    var array = (T[])structArraysByType[typeof(T)];
    accessor(ref array[index]);
}

public TResult Project<T, TResult>(int index, Projector<T, TResult> projector)
{
    var array = (T[])structArraysByType[typeof(T)];
    return projector(ref array[index]);
}

或者只是 return 对底层数组本身的引用,如果你不需要抽象它/隐藏你的 class 封装它们的事实:

public T[] GetArray<T>()
{
    return (T[])structArraysByType[typeof(T)];
}

然后您可以从中简单地访问元素:

var myThingsArray = MyStructArraysType.GetArray<MyThing>();
var someFieldValue = myThingsArray[10].SomeField;
myThingsArray[3].AnotherField = "Hello";

或者,如果没有特定原因将它们作为结构(即确保顺序缓存友好的快速访问),您可能只想使用 classes。

有一个更好的解决方案计划添加到下一版本的 C# 中,但 C# 中尚不存在 - .NET 的 "return ref" 功能已经存在,但 C# 编译器不支持。

这是跟踪该功能的问题:https://github.com/dotnet/roslyn/issues/118

这样,整个问题就变得微不足道了"return ref the result"。

(为将来添加的答案,当现有答案将变得过时(我希望),并且因为还有时间对该提议发表评论/添加/改进它!)