With the new readonly instance member features in C# 8, I try to minify unnecessary copying of struct instances in my code.
I do have some foreach
iterations over arrays of structs, and according to this answer, it means that every element is copied when iterating over the array.
I thought I can simply modify my code now to prevent the copying, like so:
// Example struct, real structs may be even bigger than 32 bytes.
struct Color
{
public int R;
public int G;
public int B;
public int A;
}
class Program
{
static void Main()
{
Color[] colors = new Color[128];
foreach (ref readonly Color color in ref colors) // note 'ref readonly' placed here
Debug.WriteLine($"Color is {color.R} {color.G} {color.B} {color.A}.");
}
}
This sadly does not compile with
CS1510 A ref or out value must be an assignable variable
However, using an indexer like this compiles:
static void Main()
{
Color[] colors = new Color[128];
for (int i = 0; i < colors.Length; i++)
{
ref readonly Color color = ref colors[i];
Debug.WriteLine($"Color is {color.R} {color.G} {color.B} {color.A}.");
}
}
Is my syntax in the foreach
alternative wrong, or is this simply not possible in C# 8 (possibly because of how the enumeration is implemented internally)?
Or is C# 8 applying some intelligence nowadays and does no longer copy the Color
instances by itself?
Modifying the array during iterationforEach() does not make a copy of the array before iterating.
1. Array elements are accessed using the Subscript variable, Similarly Structure members are accessed using dot [.] operator. Structure written inside another structure is called as nesting of two structures.
A structure may contain elements of different data types – int, char, float, double, etc. It may also contain an array as its member. Such an array is called an array within a structure. An array within a structure is a member of the structure and can be accessed just as we access other elements of the structure.
Answers. "is known that struct objects consume less memory than class objects because struct object does not need an additional memory location to store the memory address of the object created from the new operator." Yes, that's indeed a difference. For example if you have an array of 10 System.
foreach
works based on the target type's definitions rather than some internal blackboxes. We could make use of this to create by-ref enumeration support:
//using System;
public readonly struct ArrayEnumerableByRef<T>
{
private readonly T[] _target;
public ArrayEnumerableByRef(T[] target) => _target = target;
public Enumerator GetEnumerator() => new Enumerator(_target);
public struct Enumerator
{
private readonly T[] _target;
private int _index;
public Enumerator(T[] target)
{
_target = target;
_index = -1;
}
public readonly ref T Current
{
get
{
if (_target is null || _index < 0 || _index > _target.Length)
{
throw new InvalidOperationException();
}
return ref _target[_index];
}
}
public bool MoveNext() => ++_index < _target.Length;
public void Reset() => _index = -1;
}
}
public static class ArrayExtensions
{
public static ArrayEnumerableByRef<T> ToEnumerableByRef<T>(this T[] array) => new ArrayEnumerableByRef<T>(array);
}
Then we could enumerate an array with foreach
loop by reference:
static void Main()
{
var colors = new Color[128];
foreach (ref readonly var color in colors.ToEnumerableByRef())
{
Debug.WriteLine($"Color is {color.R} {color.G} {color.B} {color.A}.");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With