Почему установка свойства для перечисляемого объекта не работает?

StackOverflow https://stackoverflow.com/questions/317527

  •  11-07-2019
  •  | 
  •  

Вопрос

Я много знаю о C #, но этот ставит меня в тупик, и Google не помогает.

У меня есть неисчислимый набор объектов.Я хочу установить свойство для первого из них.Я делаю это, но когда я перечисляю весь диапазон объектов после модификации, я не вижу своих изменений.

Вот хороший пример проблемы:

    public static void GenericCollectionModifier()
    {
        // 1, 2, 3, 4... 10
        var range = Enumerable.Range(1, 10);

        // Convert range into SubItem classes
        var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});

        Write(items);  // Expect to output 1,2,3,4,5,6,7,8,9,10

        // Make a change
        items.First().MagicNumber = 42;

        Write(items);  // Expect to output 42,2,3,4,5,6,7,8,9,10
        // Actual output: 1,2,3,4,5,6,7,8,9,10
    }

    public static void Write(IEnumerable<SubItem> items)
    {
        Console.WriteLine(string.Join(", ", items.Select(item => item.MagicNumber.ToString()).ToArray()));
    }

    public class SubItem
    {
       public string Name;
       public int MagicNumber;
    }

Какой аспект C # останавливает вывод моего изменения "MagicNumber = 42"?Есть ли способ, которым я могу изменить свое изменение на "stick", не выполняя какое-то обалденное преобразование в List<> или массив?

Спасибо!-Майк

Это было полезно?

Решение

Когда вы вызываете First(), он перечисляет результат этого фрагмента кода:

Select(i => new SubItem() {Name = "foo", MagicNumber = i});

Обратите внимание, что Select является отложенным перечислителем, что означает, что он выполняет select только тогда, когда вы запрашиваете у него элемент (и делает это каждый время, когда вы спросите об этом).Результаты нигде не сохраняются, поэтому при вызове items.First() вы получаете новый SubItem пример.Когда вы затем передаете элементы для записи, он получает целую кучу новых SubItem экземпляры - не те, которые вы получали раньше.

Если вы хотите сохранить результат вашего выбора и изменить его, вам нужно сделать что-то вроде:

var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();

Другие советы

Я подозреваю, что что-то происходит на заднем плане.Скорее всего, из-за того, что IEnumerables могут быть повторены только один раз.

Работает ли это, если вы добавляете 'ToList()' после вызова Select() при назначении 'items'?

Единственное, о чем я могу думать, это о том, что items.First() передает копию SubItem вашему вместо ссылки, поэтому, когда вы устанавливаете его, изменение не выполняется.

Я должен предположить, что это как-то связано с тем, что IQueryable может быть повторен только один раз.Возможно, вы захотите попробовать изменить это:

// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});

Для

// Convert range into SubItem classes
var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();

И посмотрите, есть ли какие-то другие результаты.

Вы не можете / не должны изменять коллекцию с помощью перечислителя.Я удивлен, что это не создает исключения.

.First() - это метод, а не свойство.Он возвращает новый экземпляр объекта в первой позиции вашего перечисляемого.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top