Можно ли использовать массив структур, не копируя элементы в C # 8? - PullRequest
3 голосов
/ 23 сентября 2019

Благодаря новым функциям-членам, доступным только для чтения, в C # 8 , я пытаюсь минимизировать ненужное копирование экземпляров структуры в моем коде.

У меня есть foreach итераций над массивамиструктуры, и в соответствии с этим ответом это означает, что каждый элемент копируется при итерации по массиву.

Я подумал, что теперь я могу просто изменить свой код, чтобы предотвратить копирование, вот так:

// 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}.");
    }
}

К сожалению, это не компилируется с

CS1510  A ref or out value must be an assignable variable

Однако при использовании индекса, подобного следующему, компилируется:

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}.");
    }
}

Является ли мой синтаксис в foreachАльтернатива неверна, или это просто невозможно в C # 8 (возможно, из-за того, как перечисление реализовано внутри)?Или C # 8 в настоящее время применяет некоторые интеллектуальные возможности и больше не копирует Color экземпляры самостоятельно?

1 Ответ

2 голосов
/ 25 сентября 2019

foreach работает на основе определений целевого типа, а не некоторых внутренних черных ящиков.Мы могли бы использовать это для создания поддержки перечисления by-ref:

//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);
}

Тогда мы могли бы перечислить массив с циклом foreach по ссылке:

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}.");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...