As of C#9, I am not aware of any way to pull a struct by reference out of a generic container, including List<T>
. As Jason Olson's answer said:
The real underlying issue is that structs are a Value type, not a Reference type. So when you pull out a "reference" to the struct from the list, it is creating a new copy of the entire struct. So any changes you make on it are changing the copy, not the original version in the list.
So, this can be pretty inefficient. SuperCat's answer, even though it is correct, compounds that inefficiency by copying the updated struct back into the list.
If you are interested in maximizing the performance of structs, then use an array instead of List<T>
. The indexer in an array returns a reference to the struct and does not copy the entire struct out like the List<T>
indexer. Also, an array is more efficient than List<T>
.
If you need to grow the array over time, then create a generic class that works like List<T>
, but uses arrays underneath.
There is an alternative solution. Create a class that incorporates the structure and create public methods to call the methods of that structure for the required functionality. Use a List<T>
and specify the class for T. The structure may also be returned via a ref returns method or ref property that returns a reference to the structure.
The advantage of this approach is that it can be used with any generic data structure, like Dictionary<TKey, TValue>
. When pulling a struct out of a Dictionary<TKey, TValue>
, it also copies the struct to a new instance, just like List<T>
. I suspect that this is true for all C# generic containers.
Code example:
public struct Mutable
{
private int _x;
public Mutable(int x)
{
_x = x;
}
public int X => _x; // Property
public void IncrementX() { _x++; }
}
public class MutClass
{
public Mutable Mut;
//
public MutClass()
{
Mut = new Mutable(2);
}
public MutClass(int x)
{
Mut = new Mutable(x);
}
public ref Mutable MutRef => ref Mut; // Property
public ref Mutable GetMutStruct()
{
return ref Mut;
}
}
private static void TestClassList()
{
// This test method shows that a list of a class that holds a struct
// may be used to efficiently obtain the struct by reference.
//
var mcList = new List<MutClass>();
var mClass = new MutClass(1);
mcList.Add(mClass);
ref Mutable mutRef = ref mcList[0].MutRef;
// Increment the x value defined in the struct.
mutRef.IncrementX();
// Now verify that the X values match.
if (mutRef.X != mClass.Mut.X)
Console.Error.WriteLine("TestClassList: Error - the X values do not match.");
else
Console.Error.WriteLine("TestClassList: Success - the X values match!");
}
Output on console window:
TestClassList: Success - the X values match!
For the following line:
ref Mutable mutRef = ref mcList[0].MutRef;
I initially and inadvertently left out the ref after the equal sign. The compiler didn't complain, but it did produce a copy of the struct and the test failed when it ran. After adding the ref, it ran correctly.