Pull to refresh

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

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.
Первая статься, ошибки замечания пиши как тут принято, если будут какие вопросы отвечу в комментариях. Спасибо за внимание.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.