У меня есть несколько неуклюжий ряд утверждений о том, что по привычке я гнезду вот так:
// Cannot collide with right
if(direction.x < 0)
{
// Cannot collide with top
if(direction.y < 0)
{
// Check left and bottom
}
// Cannot collide with bottom
else if (direction.y > 0)
{
// Check left and top
}
// Cannot collide with top or bottom
else
{
// Check left only
}
}
// Cannot collide with left
else if (direction.x > 0)
{
// Cannot collide with top
if(direction.y < 0)
{
// Check right and bottom
}
// Cannot collide with bottom
else if (direction.y > 0)
{
// Check right and top
}
// Cannot collide with top or bottom
else
{
// Check right only
}
}
Однако мне трудно читать и думать, что было бы легче следовать как плоский набор утверждений if, несколько похожий на переключатель:
// Cannot collide with right or top
if(direction.x < 0 && direction.y < 0)
{
// Check left and bottom
}
// Cannot collide with right or bottom
else if(direction.x < 0 && direction.y > 0)
{
// Check left and top
}
// Cannot collide with right, top or bottom
else if(direction.x < 0)
{
// Check left only
}
// Cannot collide with left or top
else if (direction.x > 0 && direction.y < 0)
{
// Check right and bottom
}
// Cannot collide with left or bottom
else if (direction.x > 0 && direction.y > 0)
{
// Check right and top
}
// Cannot collide with left, top or bottom
else
{
// Check right only
}
Очевидным недостатком этого является то, что я повторно проверяю условие несколько раз. В этом случае он настолько мал, что я не могу представить, что это имеет значение, но мои вопросы:
Операторы AND будут содержать ярлык компилятором в С#, поэтому, если direction.x <= 0, второе предложение в ваших инструкциях if не будет оцениваться, поэтому нет необходимости в гнезде на основе "direction> 0",,
direction.x
объявлен как volatile
.
Я бы предпочел первый подход, потому что он уменьшает дубликаты кода (кроме того, что он может быть немного быстрее - в случае volatile
атрибутов переменные должны быть перечитаны).
Что произойдет, если вам нужно изменить direction.x < 0
условие direction.x > 1
(может быть, не лучший пример здесь, но вы получите идею...)? Вам нужно только изменить его в одном месте.
Также я думаю, что первый подход легче понять: когда вы следуете коду, вы знаете, что если вы находитесь в первой ветке оператора if
, то он "не может столкнуться с правильным", а при втором подходе вам всегда нужно думать через все условие, а также все условия предыдущих утверждений if
чтобы знать, если вы даже доберетесь туда.
Ваш код можно улучшить, разделив условия с помощью фактического кода, который вы хотите сделать, действительно превратив его в switch
.
Для этого мы будем использовать эти константы:
static final int LEFT = 0x01;
static final int RIGHT = 0x02;
static final int TOP = 0x10;
static final int BOTTOM = 0x20;
И код:
final int dir = (d.x < 0 ? LEFT : d.x > 0 ? RIGHT : 0)
+ (d.y < 0 ? TOP : d.y > 0 ? BOTTOM : 0);
switch (dir) {
case LEFT: // Check left only
break;
case RIGHT: // Check right only
break;
case LEFT + TOP: // Check left and top
break;
case LEFT + BOTTOM: // Check left and bottom
break;
case RIGHT + TOP: // Check right and top
break;
case RIGHT + BOTTOM: // Check right and bottom
break;
}
Преждевременная оптимизация - зло.
С точки зрения удобочитаемости я бы сделал что-то вроде
[Flags]
enum Direction
{
None = 0,
Top = 1 << 0,
Bottom = 1 << 1,
Left = 1 << 2,
Right = 1 << 3,
}
А потом
var d = (x > 0 ? Direction.Right : (x < 0 ? Direction.Left : Direction.None)) |
(y > 0 ? Direction.Top : (y < 0 ? Direction.Bottom : Direction.None));
switch(d)
{
case Direction.Left:
...
break;
case Direction.Right | Direction.Bottom:
...
break;
...
}
else { if(predicate) { ...
, вам просто не нужно{
.