Почему установка свойства для перечисляемого объекта не работает? - PullRequest
3 голосов
/ 25 ноября 2008

Я много знаю о C #, но этот вводит меня в заблуждение, и Google не помогает.

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

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

    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"? Есть ли способ, которым я могу получить свои изменения, чтобы "придерживаться", не делая прикольного преобразования в List <> или массив?

Спасибо! -Mike

Ответы [ 5 ]

8 голосов
/ 25 ноября 2008

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

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

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

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

var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();
0 голосов
/ 25 ноября 2008

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

0 голосов
/ 25 ноября 2008

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

0 голосов
/ 25 ноября 2008

Единственное, о чем я могу думать, это то, что 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();

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

0 голосов
/ 25 ноября 2008

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

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

...