Ref Enumerable
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.04.21 05:39
Оценка: 60 (2)
Внезапно задумался, а почему не работает вот такое:

foreach(var k in new int[10])
  k = 42;


Ну, ладно, получаем CS1656, Cannot assign to 'd' because it is a 'foreach iteration variable'.
Ладно, всё верно, у нас там под капотом IEnumerable<int>, который возвращает IEnumerator<int>, у которого int Current{get;}. Действительно — куда там присваивать?
Но! У нас же есть возможность сделать ref int Current {get;}.
Жаль, но вот такой код компилироваться не будет:
public static IEnumerable<ref T> RefEnumerable(this T[] data)
{
  for (var i = 0; i < data.Length; i++)
    yield return ref data[i];
}

В первой же строке у нас Unexpected token 'ref'.
Ладно, папенька, пойдём длинным путём.

    public interface IRefEnumerator<T>: IEnumerator, IDisposable
    {
        new ref T Current { get; }
    }
    public interface IRefEnumerable<T>
    {
        public IRefEnumerator<T> GetEnumerator();
    }
    public class ArrayEnumerable<T>: IRefEnumerable<T>
    {
        private T[] _data;
        public ArrayEnumerable(T[] data ) => _data = data ?? throw new ArgumentNullException(nameof(data));
        private class ArrayEnumerator<T>: IRefEnumerator<T>
        {
            private T[] _data;
            private int _pos = -1;
            public ArrayEnumerator(T[] data) => _data = data ?? throw new ArgumentNullException(nameof(data));
            public ref T Current => ref _data[_pos];
            object IEnumerator.Current => _data[_pos];
            public void Dispose(){}
            public bool MoveNext() => ++_pos < _data.Length;
            public void Reset() => _pos = -1;
        }
        public IRefEnumerator<T> GetEnumerator() => new ArrayEnumerator<T>(_data);
    }
    public static class ArrayEnumerable
    {
        public static ArrayEnumerable<T> AsRefEnumerable<T>(this T[] data) => new ArrayEnumerable<T>(data);
    }


Пока что всё компилируется.
Но вот почему
foreach(var k in (new int[10]).AsRefEnumerable())
  k = 42;

по-прежнему выдаёт CS1656? И можно ли это доработать, незначительно поправив Roslyn?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 29.04.2021 20:11 VladD2 . Предыдущая версия . Еще …
Отредактировано 29.04.2021 20:07 VladD2 . Предыдущая версия .
compiler
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.