LINQ的新功能:这是使用LINQ的正确位置吗?

时间:2013-01-23 13:05:42

标签: c# linq xna

首先,我是LINQ的新手,所以我真的不知道它的来龙去脉。我试着在一些代码中使用它,根据我的诊断,它似乎与以相同方式使用for循环一样快。但是,我不确定这会如何扩展,因为我正在使用的列表可能会非常显着地增加。

我正在使用LINQ作为碰撞检测功能的一部分(目前仍在使用中),我正在使用它来剔除列表中仅与检查相关的列表。

这是LINQ版本:

partial class Actor {
    public virtual bool checkActorsForCollision(Vector2 toCheck) {
        Vector2 floored=new Vector2((int)toCheck.X, (int)toCheck.Y);

        if(!causingCollision) // skip if this actor doesn't collide
            return false;

        foreach(
            Actor actor in
            from a in GamePlay.actors
            where a.causingCollision==true&&a.isAlive
            select a
            )
            if( // ignore offscreen collisions, we don't care about them
                (actor.location.X>GamePlay.onScreenMinimum.X)
                &&
                (actor.location.Y>GamePlay.onScreenMinimum.Y)
                &&
                (actor.location.X<GamePlay.onScreenMaximum.X)
                &&
                (actor.location.Y<GamePlay.onScreenMaximum.Y)
                )
                if(actor!=this) { // ignore collisions with self
                    Vector2 actorfloor=new Vector2((int)actor.location.X, (int)actor.location.Y);

                    if((floored.X==actorfloor.X)&&(floored.Y==actorfloor.Y))
                        return true;
                }

        return false;
    }
}

这是我以前的方法:

partial class Actor {
    public virtual bool checkActorsForCollision(Vector2 toCheck) {
        Vector2 floored=new Vector2((int)toCheck.X, (int)toCheck.Y);

        if(!causingCollision) // skip if this actor doesn't collide
            return false;

        for(int i=0; i<GamePlay.actors.Count; i++)
            if( // ignore offscreen collisions, we don't care about them
                (GamePlay.actors[i].location.X>GamePlay.onScreenMinimum.X)
                &&
                (GamePlay.actors[i].location.Y>GamePlay.onScreenMinimum.Y)
                &&
                (GamePlay.actors[i].location.X<GamePlay.onScreenMaximum.X)
                &&
                (GamePlay.actors[i].location.Y<GamePlay.onScreenMaximum.Y)
                )
                if( // ignore collisions with self
                    (GamePlay.actors[i].isAlive)
                    &&
                    (GamePlay.actors[i]!=this)
                    &&
                    (GamePlay.actors[i].causingCollision)
                    ) {
                    Vector2 actorfloor=
                        new Vector2(
                            (int)GamePlay.actors[i].location.X,
                            (int)GamePlay.actors[i].location.Y
                            );

                    if((floored.X==actorfloor.X)&&(floored.Y==actorfloor.Y))
                        return true;
                }

        return false;
    }
}

当下,要么几乎没有时间运行(但是每秒运行很多次),但随着项目的构建和变得更复杂,这将同时处理更多的对象,并且检查冲突的代码将会更详细。

5 个答案:

答案 0 :(得分:11)

你的代码看起来很不错;我不是改变工作代码的忠实粉丝,但是如果你确实想要重写它以便更容易阅读,那么我会做的就是:

首先,摘要谓词“离开屏幕”。也许让它成为GamePlay的一种方法。这种每次检查坐标是否在边界内的业务是(1)实现细节,以及(2)使您的代码难以阅读。将来你可能会有一些更复杂的机制来决定一个对象是否在屏幕上。

其次,抽象出矢量地板操作。也许让它成为Vector的一种方法。请注意,此方法应返回一个新向量,而不是改变现有向量。

第三,在向量上建立一个等于运算符。

第四,更好地命名方法。谓词的格式应为“IsFoo”或“HasFoo”。你把它称为命令,而不是问题。

第五,你根本不需要循环。

第六,说somebool == true很奇怪。只需说somebool。前者意味着“如果真的是这个布尔是真的”,这是不必要的复杂。

让我们看看这是怎么回事:

public virtual bool HasCollisionWithAnyActor(Vector2 toCheck)
{
    // "Easy out": if this actor does not cause collisions then
    // we know that it is not colliding with any actor.
    if (!causingCollision)
      return false;

    Vector2 floored = toCheck.Floor();

    var collidingActors = 
      from actor in GamePlay.actors
      where actor != this
      where actor.causingCollision
      where actor.isAlive
      where GamePlay.IsOnScreen(actor.location)
      where actor.location.Floor() == floored
      select actor;

    return collidingActors.Any();
}

看一下读取比你的方法版本更容易!没有一个乱七八糟的X和Y坐标。 让辅助方法完成所有这些操作。现在代码清楚地表达了语义:告诉我屏幕上是否与其他生活的,引起碰撞的演员发生任何碰撞。

答案 1 :(得分:2)

这里LINQ版本比前一版本更快,因为您忘记创建一个本地变量来存储GamePlay.actors[i]。然后,如果在actors循环中进行了很多次,则访问for数组。

public virtual bool checkActorsForCollision(Vector2 toCheck)
{
    Vector2 floored = new Vector2((int) toCheck.X, (int) toCheck.Y);

    if (!causingCollision) // skip if this actor doesn't collide
    return false;

    for (int i = 0; i < GamePlay.actors.Count; i++)
    {
        Actor actor = GamePlay.actors[i];
        // ignore offscreen collisions, we don't care about them
        if ((actor.location.X > GamePlay.onScreenMinimum.X) &&
            (actor.location.Y > GamePlay.onScreenMinimum.Y) &&
            (actor.location.X < GamePlay.onScreenMaximum.X) &&
            (actor.location.Y < GamePlay.onScreenMaximum.Y))
        {

            if ((actor.isAlive) && (actor != this) &&
                (actor.causingCollision)) // ignore collisions with self
            {
                Vector2 actorfloor =
                  new Vector2((int) actor.location.X,
                              (int) actor.location.Y);
                if ((floored.X == actorfloor.X) &&
                    (floored.Y == actorfloor.Y))
                  return true;
            }
        }
    }
    return false;
}

现在,LINQ一般表现非常好。但在某些情况下,使用经典for版本会更好。您必须在代码的可见性和性能之间找到适当的平衡(通过比较LINQ版本和for版本)。就我而言,我使用和滥用LINQ因为我非常喜欢它的语法和它的可用性。

这是一个完整的LINQ版本:

return (from actor in GamePlay.actors
        where actor.causingCollision && a.isAlive
        where actor != this
        where (actor.location.X > GamePlay.onScreenMinimum.X) &&
              (actor.location.Y > GamePlay.onScreenMinimum.Y) &&
              (actor.location.X < GamePlay.onScreenMaximum.X) &&
              (actor.location.Y < GamePlay.onScreenMaximum.Y)
        select new Vector2((int)actor.location.X,
                           (int)actor.location.Y)).Any(actorfloor => (floored.X == actorfloor.X) &&
                                                                     (floored.Y == actorfloor.Y));

答案 2 :(得分:1)

我认为在这里使用LINQ没有任何问题。如果你想知道自己是否在提高性能,你应该对Diagnostics.StopWatch进行一些测试。

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx

此外,您可以使用更多这样的LINQ来使功能更紧凑。

return (from actor in (from a in GamePlay.actors
                                   where a.CausingCollision == true && a.IsAlive
                                   select a)
                    where (actor.location.X > GamePlay.onScreenMinimum.X) && (actor.location.Y > GamePlay.onScreenMinimum.Y) && (actor.location.X < GamePlay.onScreenMaximum.X) && (actor.location.Y < GamePlay.onScreenMaximum.Y)
                    where actor != this
                    select new Vector2((int) actor.location.X, (int) actor.location.Y)).Any(actorfloor => (floored.X == actorfloor.X) && (floored.Y == actorfloor.Y));

答案 3 :(得分:1)

我不确定我是否理解该问题,但总的来说,我认为使用LINQ而不是创建额外的循环和if / else调用总是更好的解决方案。

供参考: 您也可以使用LINQ-Methodes代替关键词,例如:

foreach(Actor actor in from a in GamePlay.actors
                           where a.causingCollision == true 
                           && a.isAlive
                           select a)
{
  //...
}

与:

相同
foreach(Actor actor in GamePlay.actors.Where( a => a.causingCollision == true && a.isAlive))
{
  //...
}

两种情况下的返回都是过滤的IEnumerable。我个人认为,这些方法更容易阅读和理解。在Where(...)中,你可以放置一个完整的lambda表达式来完全选择你想要的Actors。

答案 4 :(得分:1)

LINQ已经过优化,其输出与手动编写你的fors相同。 顺便说一下,扩展方法更具可读性。您的示例将如下所示:

public virtual bool checkActorsForCollision(Vector2 toCheck)
{
    Vector2 floored = new Vector2((int)toCheck.X, (int)toCheck.Y);

    // skip if this actor doesn't collide
    if (!causingCollision)
    {
        return false;
    }

    return GamePlay.actors.Where( actor =>    
            (actor.causingCollision == true) 
            && actor.isAlive
            // ignore offscreen collisions, we don't care about them
            && (actor.location.X > GamePlay.onScreenMinimum.X) 
            && (actor.location.Y > GamePlay.onScreenMinimum.Y)          
            && (actor.location.X < GamePlay.onScreenMaximum.X) 
            && (actor.location.Y < GamePlay.onScreenMaximum.Y)
            // ignore collisions with self
            && (actor != this) )
        .Select( actor => new Vector2((int)actor.location.X, (int)actor.location.Y) )
        .Any( actorFloor => 
            (floored.X == actorfloor.X) 
            && (floored.Y == actorfloor.Y));
}

如果您没有对应用程序进行概要分析,则没有理由不将LINQ用于“性能原因”。