Привет всем. Сегодня мне попалась задача, которая дала понять мне что, не все тонкости я еще знаю.
Но при этом не оставила меня равнодушным к этой проблеме.
Суть проблемы заключается в том что при работе кода ниже, происходит странная работа 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.
Первая статься, ошибки замечания пиши как тут принято, если будут какие вопросы отвечу в комментариях. Спасибо за внимание.
Автор: Жрец