存储对结构数组的 C# 引用并检索它 - 可以不复制吗?
Storing a C# reference to an array of structs and retrieving it - possible without copying?
更新:下一个版本的 C# 有一个正在考虑中的功能,可以直接回答这个问题。 c.f。下面的答案。
要求:
- 应用程序数据存储在结构数组中。应用程序中的每种数据类型都有一个 AoS(例如,一个用于 MyStruct1,另一个用于 MyStruct2,等等)
- 结构是在运行时创建的;我们在应用程序中编写的代码越多,就会越多。
- 我需要一个 class 来保存对所有 AoS 的引用,并允许我在这些 AoS 中设置和获取单个结构
- 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;
}
备注:
- 我需要确保 C# 将其视为值类型数组,而不是对象数组 ("don't you DARE go making an array of boxed objects around my structs!")。据我了解:通用 T[] 确保(如预期的那样)
- 除了 System.Array,我不知道如何表达类型 "this will be an array of structs, but I can't tell you which structs at compile time"。 System.Array 有效 -- 但也许还有其他选择?
- 为了索引生成的数组,我必须类型转换回 T[]。我害怕这种类型转换 MIGHT 会装箱结构数组;我知道如果它是 (T) 而不是 (T[]),它肯定会 box;希望它不会用 T[] ?
- 或者,我可以使用 System.Array 方法,它肯定会封装传入和传出结构。这是一个相当大的问题(尽管如果这是使 C# 与 Array-of-struct 一起工作的唯一方法,我可以解决它)
据我所知,你正在做的应该工作正常,但是是的,当你调用 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"。
(为将来添加的答案,当现有答案将变得过时(我希望),并且因为还有时间对该提议发表评论/添加/改进它!)
更新:下一个版本的 C# 有一个正在考虑中的功能,可以直接回答这个问题。 c.f。下面的答案。
要求:
- 应用程序数据存储在结构数组中。应用程序中的每种数据类型都有一个 AoS(例如,一个用于 MyStruct1,另一个用于 MyStruct2,等等)
- 结构是在运行时创建的;我们在应用程序中编写的代码越多,就会越多。
- 我需要一个 class 来保存对所有 AoS 的引用,并允许我在这些 AoS 中设置和获取单个结构
- 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;
}
备注:
- 我需要确保 C# 将其视为值类型数组,而不是对象数组 ("don't you DARE go making an array of boxed objects around my structs!")。据我了解:通用 T[] 确保(如预期的那样)
- 除了 System.Array,我不知道如何表达类型 "this will be an array of structs, but I can't tell you which structs at compile time"。 System.Array 有效 -- 但也许还有其他选择?
- 为了索引生成的数组,我必须类型转换回 T[]。我害怕这种类型转换 MIGHT 会装箱结构数组;我知道如果它是 (T) 而不是 (T[]),它肯定会 box;希望它不会用 T[] ?
- 或者,我可以使用 System.Array 方法,它肯定会封装传入和传出结构。这是一个相当大的问题(尽管如果这是使 C# 与 Array-of-struct 一起工作的唯一方法,我可以解决它)
据我所知,你正在做的应该工作正常,但是是的,当你调用 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"。
(为将来添加的答案,当现有答案将变得过时(我希望),并且因为还有时间对该提议发表评论/添加/改进它!)