Странное поведение Linq запроса под Debug

в 18:27, , рубрики: C#

image

Привет всем. Сегодня мне попалась задача, которая дала понять мне что, не все тонкости я еще знаю.

Но при этом не оставила меня равнодушным к этой проблеме.

Суть проблемы заключается в том что при работе кода ниже, происходит странная работа IEnumerable.

Который меняет значения в исходных данных, и при этом все это происходит под Debug режимом. Использовалась Visual Studio 2017, возможно в других версиях это не воспроизведется.

Внутри есть несколько видео, показывающие работу ошибок.

Начнем с того что посмотрим на код ниже, он должен сгруппировать элементы и вывести сгруппированные элементы и показать сумму их Value.

    class Program
    {
        static void Main(string[] args)
        {
            var testItems = new List<Test>
            {
                new Test{Id = 1, GroupId = 1, Value = 2},
                new Test{Id = 2, GroupId = 1, Value = 2},
                new Test{Id = 3, GroupId = 1, Value = 2},
                new Test{Id = 4, GroupId = 2, Value = 2},
            };

            var items = testItems.GroupBy(i => i.GroupId).Select(group =>
            {
                var i = group.First();
                i.Value = group.Sum(g => g.Value);
                return i;
            });
        }
    }

    public class Test
    {
        public int Id { get; set; }
        public int GroupId { get; set; }
        public int Value { get; set; }
    }
}

Давайте теперь посмотрим на результат переменной items.Если прочитать код, то понятно что будет 2 строки.

[0] GroupID=1,ID=1,value=6
[1] GroupID=2,ID=2,value=2

Если поставить точку остановки после переменной items и в debug посмотреть какой будет результат то мы увидим.

[0] GroupID=1,ID=1,value=10
[1] GroupID=2,ID=2,value=2

Для наглядности записал видео.

Первая мысль которая может быть, что это ссылочный тип и что элемент Value элемента ID=1 изменился на сумму первой группы, то есть 6-ть и после прибавилось еще ID=2 и ID=4 что дает в сумме 10.

Но появляется следующая проблема. Если оставить только одну группу то не будет как мы думаем.

 var testItems = new List<Test>
            {
                new Test{Id = 1, GroupId = 1, Value = 2},
                new Test{Id = 2, GroupId = 1, Value = 2},
                new Test{Id = 3, GroupId = 1, Value = 2}
            };

Результат будет следующим.

[0] GroupID=1,ID=1,value=14

При этом каждое следующее обращение будет итеративно прибавляться, 14,26,38,50,62,74,86.
Вопрос который не был решен, по какой причине под Debug режимом, объекты изменяются и присваиваются итеративно. Хотелось чтобы кто то объяснил это.

Есть также еще одна проблема.В которой происходит фантомный вызов linq объекта.Код ниже показывает проблему.


class Program
    {
        private static int counter = 0;


        public static  Task<int> rrr(int x)
        {
            Console.WriteLine(x);
            return 
            Task.FromResult(counter++);
        }

        private static int counterr = 0;

        static void Main(string[] args)
        {



            var testItems = new List<Test>
            {
                new Test{Id = 1, GroupId = 1, Value = 2}

            };

            var items = testItems.GroupBy(i => i.GroupId).Select(group =>
            {
                Console.WriteLine("Вызвано " + ++counterr);

                return group;
            });
            ;

            Console.ReadKey();
        }
    }

    public class Test
    {
        public int Id { get; set; }
        public int GroupId { get; set; }
        public int Value { get; set; }
    }
}

Глядя на этот код вы можете подумать, в консоль выведется только одна строчка «Вызвано n», но и тут проблемы.

Если вы выполните этот код, по вы увидите что вызовов было три. Хотя их должно быть два.
Первый при работе и второй раз когда мы смотрели результат в ResultView.

При этом если добавить еще одну строчку в testItems

  var testItems = new List<Test>
            {
                new Test{Id = 1, GroupId = 1, Value = 2},
 	        new Test{Id = 2, GroupId = 2, Value = 2}

            };

То будет не шесть вызовов как вам может показаться, а всего четыре вызова. При дальнейшем прибавлении элементов, кол-во проходов будет увеличиться на два.

Четыре вызова, шесть вызовов, восемь вызовов.

Решение этих проблем найдено, просто переменную items нужно привести либо к toArray() либо toList(). Но вот почему такое происходит под Debug не очень понятно, и хотелось бы чтобы кто то смог объяснить это.

P.S.
Первая статься, ошибки замечания пиши как тут принято, если будут какие вопросы отвечу в комментариях. Спасибо за внимание.

Автор: Жрец

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js