Как изменить свойства структуры в цикле foreach? - PullRequest
0 голосов
/ 11 мая 2018

Я бы хотел, чтобы следующий код работал:

foreach (Rect item in VM.Cubes)
{
     VM.FallDown(ref item);
}

class VM
{
   public void FallDown(ref Rect entity)
    {
        if ((entity.Y + entity.Height < 800))
        {
            entity.Y++;
        }
    } 
}

Поскольку Rect является структурой, это тип значения, и я могу изменить его свойство Y только в том случае, если при вызове метода я передаю параметры по ссылке.К сожалению, foreach не любит изменять свои элементы при прохождении через них, но поскольку VM.Cubes также является списком, я также не могу использовать обычный цикл for.

Что я должен сделать, чтобы он работал?

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Я думал, что я мог бы звонить здесь ...

Хотя я согласен Неизменяемые структуры имеют смысл во многих ситуациях, но удобство чтения и обслуживания всегда должны учитывать ваши соображения. Если ваша цель - производительность, то создание новой структуры в Linq Select не складывается (в этой ситуации). Вы просто выполняете больше выделений и создаете больше мусора.

В любой нормальной ситуации это, вероятно, не имеет значения. Однако, «если» вы заинтересованы в производительности и у вас есть инструкция и цикл OCD , то я бы рассмотрел альтернативные варианты и оценил бы ваши решения.

Учитывая следующие 3 метода

public void ByRef(ref Rect entity)
{
   if (entity.Y + entity.Height > 800)
   {
      entity.Y++;
   }
}

public Rect ByImutableReturn(Rect entity)
{
   return entity.Y + entity.Height > 800 ? new Rect(entity.Height, entity.Y + 1) : entity;
}

public Rect ByReturn(Rect entity)
{
   if (entity.Y + entity.Height > 800)
   {
      entity.Y++;
   }

   return entity;
}

LinqImutableReturn

protected override List<Rect> InternalRun()
{
   return Input.Cubes = Input.Cubes.Select(r => Input.ByImutableReturn(r))
                             .ToList();
}

LinqReturn

protected override List<Rect> InternalRun()
{
   return Input.Cubes = Input.Cubes.Select(r => Input.ByReturn(r))
                             .ToList();
}

ForLoopByRef

protected override List<Rect> InternalRun()
{
   for (var index = 0; index < Input.Cubes.Count; index++)
   {
      var t = Input.Cubes[index];
      Input.ByRef(ref t);
      Input.Cubes[index] = t;
   }

   return Input.Cubes.ToList();
}

ForLoopImutableReturn

protected override List<Rect> InternalRun()
{
   for (var index = 0; index < Input.Cubes.Count; index++)
   {
      Input.Cubes[index] = Input.ByImutableReturn(Input.Cubes[index]);
   }

   return Input.Cubes.ToList();
}

ForLoopReturn

protected override List<Rect> InternalRun()
{
   for (var index = 0; index < Input.Cubes.Count; index++)
   {
      Input.Cubes[index] = Input.ByReturn(Input.Cubes[index]);
   }

   return Input.Cubes.ToList();
}

Результаты

Mode            : Release
Test Framework  : .NET Framework 4.7.1
Benchmarks runs : 100 times (averaged/scale)

Scale : 10,000
Name                  |     Time |    Range | StdDev |    Cycles
--------------------------------------------------------------------------
ForLoopByRef          | 0.073 ms | 0.001 ms |   0.07 |   244,964
ForLoopReturn         | 0.097 ms | 0.006 ms |   0.05 |   332,372
ForLoopImutableReturn | 0.116 ms | 0.003 ms |   0.08 |   388,188
LinqImutableReturn    | 0.325 ms | 0.007 ms |   0.25 | 1,117,130
LinqReturn            | 0.347 ms | 0.002 ms |   0.07 | 1,195,351


Scale : 100,000
Name                  |     Time |    Range | StdDev |     Cycles
---------------------------------------------------------------------------
ForLoopByRef          | 0.635 ms | 0.168 ms |   0.11 |  2,215,066
ForLoopImutableReturn | 0.867 ms | 0.175 ms |   0.10 |  3,027,096
ForLoopReturn         | 0.890 ms | 0.225 ms |   0.09 |  3,109,831
LinqReturn            | 2.957 ms | 0.166 ms |   0.17 | 10,347,672
LinqImutableReturn    | 3.084 ms | 0.219 ms |   0.40 | 10,780,304


Scale : 1,000,000
Name                  |      Time |    Range | StdDev |      Cycles
-----------------------------------------------------------------------------
ForLoopByRef          |  6.624 ms | 1.685 ms |   0.83 |  23,156,409
ForLoopImutableReturn |  9.574 ms | 1.678 ms |   0.82 |  33,503,375
ForLoopReturn         |  9.811 ms | 2.290 ms |   0.86 |  34,324,963
LinqImutableReturn    | 32.463 ms | 1.401 ms |   1.11 | 113,246,111
LinqReturn            | 32.973 ms | 0.830 ms |   1.18 | 114,892,311

Резюме

Я бы взял эти результаты с крошкой соли. Вещи не так чисты, когда вы смотрите на высокопроизводительный код. Но мораль этой истории в том, что если вы стремитесь к производительности, а не к удобству чтения и обслуживания, вам нужно тестировать код в реальных ситуациях.

0 голосов
/ 11 мая 2018

Вы можете просто создать новый список.Это позволяет вам рассматривать вашу структуру Rect как неизменную, что считается хорошей идеей для структур в c #.

VM.Cubes = VM.Cubes.Select
( 
    r => VM.FallDown(r)
)
.ToList();

Затем определите FallDown как:

public Rect FallDown(Rect input)
{
    if (input.Y >= 800) return input;
    return new Rect(input.X, input.Y+1);
}

Это может быть нелогичным (кажется, что это может быть плохо для памяти или производительности), но так работают все функциональные языки программирования, икак LINQ был разработан, чтобы работать, и в целом будет работать просто отлично и мог даже работать лучше, чем обновление на месте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...