среда, 26 августа 2009 г.

C# : foreach и делегаты

Сколько уже не говорили на эту тему, а сюрпризы все равно находятся. Про немутабельность переменной цикла думаю рассказывать не надо. Однако даже зная этот факт можно легко попасть в неприятную ситуацию...
Дело было так. Был словарь(Dictionary) в котором содержались Id объектов и дополнительная информация. Был также массив других объектов, в котором у объектов с Id из словаря нужно было подписаться на событие. Обработчик простой, поэтому решил прям в методе создать делегат. Тело цикла вышло что-то вроде этого:

CalcHandler calc = delegate(object sender,CalcEventArgs e){
e.NewValue += e.BaseValue * pair.Value;
};
obj[pair.Key].Calc += calc;

Однако когда написал тесты, выяснилось, что в качестве pair.Value всегда выступает последнее ее значение в этом цикле! Попробовал добавить промежуточные переменные, но оптимизация все съела и толку не было. В итоге пришлось переписать цикл на for() в результате чего все отлично заработало.
Так что товарищи, хорошенько подумайте, прежде чем использовать foreach! Я не призываю от него отказываться, ведь должна же быть и от него польза :) но прежде чем использовать - обдумайте все возможные варианты, ведь если нет такого рода тонкостей, то возможно в будущем придется изменять код, а переписывать цикл, особенно если он порос разными хитростями, ой как лениво...

3 комментария:

Gengzu комментирует...

мне кажется тут проблема не в foreach, а в неправильном архитектурном подходе.

4ybaka комментирует...

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

4ybaka комментирует...

Наткнулся на статью в MSDN по этому поводу.
http://blogs.msdn.com/ruericlippert/archive/2009/11/12/9983705.aspx