存储一个C#引用到一个结构数组并检索它 - 可能没有复制?

时间:2022-10-11 21:35:34

UPDATE: the next version of C# has a feature under consideration that would directly answer this issue. c.f. answers below.

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


Requirements:

  1. App data is stored in arrays-of-structs. There is one AoS for each type of data in the app (e.g. one for MyStruct1, another for MyStruct2, etc)
  2. 应用程序数据存储在结构数组中。应用程序中的每种类型的数据都有一个AoS(例如,一个用于MyStruct1,另一个用于MyStruct2等)

  3. The structs are created at runtime; the more code we write in the app, the more there will be.
  4. 结构在运行时创建;我们在应用程序中编写的代码越多,就会越多。

  5. I need one class to hold references to ALL the AoS's, and allow me to set and get individual structs within those AoS's
  6. 我需要一个类来保存对所有AoS的引用,并允许我在这些AoS中设置和获取单独的结构

  7. The AoS's tend to be large (1,000's of structs per array); copying those AoS's around would be a total fail - they should never be copied! (they never need to!)
  8. AoS往往很大(每个阵列有1000个结构);复制那些AoS将彻底失败 - 它们永远不应被复制! (他们永远不需要!)

I have code that compiles and runs, and it works ... but is C# silently copying the AoS's under the hood every time I access them? (see below for full source)

我有编译和运行的代码,它的工作原理......但是每当我访问A#时,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;
}

Notes:

  • I need to ensure C# sees this as an array of value-types, instead of an array of objects ("don't you DARE go making an array of boxed objects around my structs!"). As I understand it: Generic T[] ensures that (as expected)
  • 我需要确保C#将其视为一个值类型数组,而不是一个对象数组(“你不要在我的结构周围制作一个盒装对象数组!”)。据我了解:Generic T []确保(如预期的那样)

  • I couldn't figure out how to express the type "this will be an array of structs, but I can't tell you which structs at compile time" other than System.Array. System.Array works -- but maybe there are alternatives?
  • 我无法弄清楚如何表达类型“这将是一个结构数组,但我不能告诉你除了System.Array之外的编译时结构。” System.Array有效 - 但也许有其他选择?

  • In order to index the resulting array, I have to typecast back to T[]. I am scared that this typecast MIGHT be boxing the Array-of-Structs; I know that if it were (T) instead of (T[]), it would definitely box; hopefully it doesn't do that with T[] ?
  • 为了索引生成的数组,我必须回输到T []。我害怕这个类型可能是拳击结构阵列;我知道如果它是(T)而不是(T []),那肯定是盒子;希望它与T []没有那样做?

  • Alternatively, I can use the System.Array methods, which definitely boxes the incoming and outgoing struct. This is a fairly major problem (although I could workaround it if were the only way to make C# work with Array-of-struct)
  • 或者,我可以使用System.Array方法,它绝对包含传入和传出结构。这是一个相当大的问题(虽然我可以解决它,如果是使C#与Array-of-struct一起工作的唯一方法)

2 个解决方案

#1


2  

As far as I can see, what you are doing should work fine, but yes it will return a copy of a struct T instance when you call Get, and perform a replacement using a stack based instance when you call Set. Unless your structs are huge, this should not be a problem.

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

If they are huge and you want to

如果它们很大并且你想要

  • Read (some) properties of one of a struct instance in your array without creating a copy of it.
  • 读取数组中某个结构实例的(某些)属性,而不创建它的副本。

  • Update some of it's fields (and your structs are not supposed to be immutable, which is generally a bad idea, but there are good reasons for doing it)
  • 更新它的一些字段(你的结构不应该是不可变的,这通常是一个坏主意,但有很好的理由这样做)

then you can add the following to your 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]);
}

Or simply return a reference to the underlying array itself, if you don't need to abstract it / hide the fact that your class encapsulates them:

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

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

From which you can then simply access the elements:

然后您可以从中轻松访问元素:

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

Alternatively, if there is no specific reason for them to be structs (i.e. to ensure sequential cache friendly fast access), you might want to simply use classes.

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

#2


1  

There is a much better solution that is planned for adding to next version of C#, but does not yet exist in C# - the "return ref" feature of .NET already exists, but isn't supported by the C# compiler.

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

Here's the Issue for tracking that feature: https://github.com/dotnet/roslyn/issues/118

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

With that, the entire problem becomes trivial "return ref the result".

有了这个,整个问题变得微不足道“返回结果”。

(answer added for future, when the existing answer will become outdated (I hope), and because there's still time to comment on that proposal / add to it / improve it!)

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

#1


2  

As far as I can see, what you are doing should work fine, but yes it will return a copy of a struct T instance when you call Get, and perform a replacement using a stack based instance when you call Set. Unless your structs are huge, this should not be a problem.

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

If they are huge and you want to

如果它们很大并且你想要

  • Read (some) properties of one of a struct instance in your array without creating a copy of it.
  • 读取数组中某个结构实例的(某些)属性,而不创建它的副本。

  • Update some of it's fields (and your structs are not supposed to be immutable, which is generally a bad idea, but there are good reasons for doing it)
  • 更新它的一些字段(你的结构不应该是不可变的,这通常是一个坏主意,但有很好的理由这样做)

then you can add the following to your 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]);
}

Or simply return a reference to the underlying array itself, if you don't need to abstract it / hide the fact that your class encapsulates them:

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

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

From which you can then simply access the elements:

然后您可以从中轻松访问元素:

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

Alternatively, if there is no specific reason for them to be structs (i.e. to ensure sequential cache friendly fast access), you might want to simply use classes.

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

#2


1  

There is a much better solution that is planned for adding to next version of C#, but does not yet exist in C# - the "return ref" feature of .NET already exists, but isn't supported by the C# compiler.

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

Here's the Issue for tracking that feature: https://github.com/dotnet/roslyn/issues/118

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

With that, the entire problem becomes trivial "return ref the result".

有了这个,整个问题变得微不足道“返回结果”。

(answer added for future, when the existing answer will become outdated (I hope), and because there's still time to comment on that proposal / add to it / improve it!)

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