Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

arrays

c#

struct

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


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. The structs are created at runtime; the more code we write in the app, the more there will be.
  3. 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
  4. 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!)

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)

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)
  • 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?
  • 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[] ?
  • 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)
like image 814
Adam Avatar asked Sep 28 '22 02:09

Adam


1 Answers

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.

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.

like image 123
Alex Avatar answered Oct 19 '22 23:10

Alex