C# ссылка на переменную цикла

можно ли в C# что-то вроде следующего

foreach (ref string var in arr) {
    var = "new value";
}

чтобы переменная var рассматривалась как ссылка и назначение var изменило бы элемент массива?

5 ответов


нет такой конструкции для обновление цикл; итератор доступен только для чтения. Например, следующее обеспечивает совершенно допустимый итератор:

public IEnumerable<int> Get1Thru5() {
    yield return 1; yield return 2; yield return 3;
    yield return 4; yield return 5;
}

как бы его обновить? что это обновление?

Если данные являются массивом / списком / etc, то что-то вроде:

for(int i = 0 ; i < arr.Length ; i++) {
    arr[i] = "new value";
}

или другие варианты в зависимости от конкретного контейнера.


обновление; в пуш расширение метод:

public static void UpdateAll<T>(this IList<T> list, Func<T, T> operation) {
    for (int i = 0; i < list.Count; i++) {
        list[i] = operation(list[i]);
    }
}
static void Main() {
    string[] arr = { "abc", "def", "ghi" };
    arr.UpdateAll(s => "new value");
    foreach (string s in arr) Console.WriteLine(s);
}

нет. The foreach оператор - это просто синтаксический сахар поверх IEnumerable интерфейс. Этот интерфейс определяет метод для получения en IEnumerator, который, в свою очередь, есть методы, чтобы сделать только для чтения перечисление:

  • Current: object
  • MoveNext (): bool
  • Reset() : void

foreach(string s in strings)
{
  Console.WriteLine(s);
}

- это ярлык компилятор:

IEnumerator e = strings.GetEnumerator();
string s;
while(e.MoveNext())
{
  s = e.Current;
  Console.WriteLine(s);
}

С IEnumerator.Current-это свойство только для получения, которое вы не можете установить.

// Non-generic IEnumerator shown.
interface IEnumerator
{
  bool MoveNext();
  object Current { get; }
  void Reset();
}

если вы хотите поддерживать обновляемый перечислитель, вам нужно будет создать его самостоятельно , но вы не сможете использовать с ним "foreach", и вам придется реализовать обертки вокруг всех общих классов IEnumerable.

вам придется проанализировать текущую ситуацию и выяснить, как обновить. Если вы используете интерфейс IList вы можете сделать:

for(int i = 0; i < strings.Count; ++i)
{
  string s = strings[i];
  //do work
  s = s.ToUpperInvariant();
  strings[i] = s;
}

в случае строки Нет; C# строки неизменяемы (не могут быть измененный.) Если бы вы перечисляли объекты другого, изменяемого типа, вы можете изменить свойства этих объекты.

просто чтобы проиллюстрировать, о чем говорит Джейкоб. Рассмотрим фрагмент кода ниже:

class MyInt
{
    public int Val { get; set; }
    public MyInt(int val) { this.Val = val; }
}

class Program
{
    static void Main(string[] args)
    {
        MyInt[] array = new MyInt[] { new MyInt(1), new MyInt(2) };

        foreach (var obj in array) Console.Write("{0}\t", obj.Val);

        foreach (var obj in array)
        { 
            obj = new MyInt(100); // This doesn't compile! the reference is read only
            obj.Val *= 10; // This works just fine!
        }

        foreach (var obj in array) Console.Write("{0}\t", obj.Val);
    }
}

действительно, если вы попытаетесь назначить "obj", как указано выше, вы получите ошибку времени компиляции. Но ничто не мешает вам изменять свойства MyInt через ссылку "obj"


в случае строки нет; строки C# неизменяемы (не могут быть изменены). При перечислении объектов другого изменяемого типа можно изменить свойства этих объектов.