【问题标题】:Implementing IQueryable and ASTs for finding entities in a game实现 IQueryable 和 AST 以在游戏中查找实体
【发布时间】:2018-10-10 15:04:39
【问题描述】:

据我了解,在某些企业 C# 代码中,有一种方法可以使用 IQueryableExpression 将 LINQ 查询转换为 AST,然后再转换为 SQL 或类似的东西。
在我看来是这样的:

无视数据库实现的代码 -> 黑魔法 -> 优化的 SQL

我想了解黑魔法并将其应用到游戏中。这是我的场景:

class Entity { public Vector2 position; }

class Chunk {
   const int CHUNK_SIZE = 16;
   public Vector2 position;  // chunk position is multiple of CHUNK_SIZE
   public List<Entity> entities;
}

class World {
   public Chunk[,] chunks; // Let's imagine this is a 256x256 array of chunks.
   public IEnumerable<Entity> Entities {
      get {
         return chunks.SelectMany(c => c.entities);
      }
   }
}

class SomewhereElse {
   void NotVerySmartCode() {
      var someArbitraryEntities = world.Entities
         .Where(e => e.position.x > 213 && e.position.x < 247
            && e.position.y > 198 && e.position.y < 212);

      foreach (var e in someArbitraryEntities) { // slooooooow }
   }
}

NotVerySmartCode 查询World.Entities 时,枚举器将遍历所有块和所有实体,并对每个块执行Where lambda。

很明显,如果Where lambda 仅在位置在 208

有没有办法智能地解释 LINQ 并执行此优化?我可以以某种方式实现IQueryable 并使用Expression 来执行一些黑魔法吗?

对不起,如果我没有任何意义,但我不明白如何将类似于上述查询的 LINQ 查询转换为高效的 SQL。

【问题讨论】:

  • 我不确定我是否没有正确理解您,或者您是否真的想针对内存中的集合运行 SQL。 IQueryable 用于外部查询,是的,通常是数据库,而不是针对内存数据的查询
  • 当然,您可以在优化和代码的尖端之间创建一个间接层,但是 IMO 这会导致未来的维护风险。归根结底,您所说的效率之所以会发生,是因为数据库可以利用为搜索空间量身定制的索引。因此,您最好将时间花在寻找索引/分区搜索空间的有效方法上,例如四叉树等。
  • @CamiloTerevinto 不,我不想对我的内存数据运行 SQL,但我希望能够以这种方式操作表达式树并找到优化。
  • @spender 嗯,是的,确切地说,我想优化 LINQ 查询来存储实体。这不是由四叉树或任何其他数据结构解决的问题,因为使用当前设置在任何位置快速找到实体是微不足道的(只需在某个位置查找块并迭代其实体) ,但这只有在代码知道数据结构的情况下才有效。如果我只知道存在一些实体,并且不知道它们存储在块中,我将如何优化幼稚的 LINQ 查询?

标签: c# linq unity3d iqueryable


【解决方案1】:

好的,当然我在发布问题半小时后就想通了。

Func<int, int> addThree = (a) => a + 3; // This compiles to IL
Expression<Func<int, int>> addTwo = (a) => a + 2; // This compiles to an AST

我们可以浏览 AST 并解释它:

Debug.Log(addTwo.Body); // (a + 2)
Debug.Log(addTwo.Type); // Func<int, int>
Debug.Log(addTwo.Body.NodeType); // Add
Debug.Log(addTwo.Body is BinaryExpression); // True
Debug.Log(((BinaryExpression)addTwo.Body).Left.NodeType); // Parameter
Debug.Log(((BinaryExpression)addTwo.Body).Right.NodeType); // Constant
Debug.Log(((ConstantExpression)((BinaryExpression)addTwo.Body).Right).Value); // 2

我们可以编译 AST:

return addTwo.Compile()(2); // 4

理论上,我们还可以检测其中的模式并优化它们。
或者我们可以将它们翻译成 SQL。

基本上,我缺少的信息是,当分配给 Expression 时,lambda 表达式会变成与分配给委托时不同的东西。

【讨论】:

  • 我仍然不知道您的目标是什么,但请为您的自我回答投上一票。
  • @spender 谢谢。好吧,这个想法是,想要过滤某些数据(在本例中为实体)的程序员不必知道数据的存储方式。理想情况下,他们应该能够为他们想要的数据指定一些要求,系统将找出获取数据的最佳方式。基本上,我想应用一个数据库的IDEA。
猜你喜欢
  • 1970-01-01
  • 2011-05-05
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 2019-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多