与Linq混淆C#Lambda表达式

时间:2019-12-27 12:19:44

标签: c# linq unity3d lambda

我试图理解这部分代码:

int[] triangles = mesh.triangles;
Color32[] colors = mesh.colors32;
IEnumerable<IGrouping<byte, int>> hierarchyMap = colors
        .Select((color, index) => new { color, index })
        .GroupBy(c => c.color.g, c => c.index);

IEnumerable<int> leafIndexes = hierarchyMap
        .Where(x => x.Key == 255)
        .SelectMany(x=>x);

Dictionary<int, HashSet<int>> faces = triangles
        .Select((vert, index) => new { vert, index })
        .GroupBy(g => g.index / 3, i => i.vert)
        .Where(g=> leafIndexes.Any(leaf=>g.Contains(leaf)))
        .ToDictionary(g=>g.Key, g=>new HashSet<int>(g));

这些代码对我来说就像魔术。似乎“ c”代表颜色中的每个元素,类型Color32确实具有一个名为color的属性。但是在最后一行,triangle是类型int的数组,int类型如何具有称为vert的属性?并且intColor32都没有属性index

我对这些表达式很困惑,我只在网上找到一些简单的Lambda表达式示例。阅读示例后,我仍然停留在这些代码上。

3 个答案:

答案 0 :(得分:1)

正如我在评论中说的那样,此代码本身就太“聪明”了,由于重复的迭代,它非常非常慢。

前两个查询是多余的,最后,leafIndexes仅包含索引color 255,或更确切地说是0x0000FFSelect((item,index)=>...)将项目及其的索引传递给lambda。那两个查询可能只是:

var leafIndexes=colors.Select((color,idx)=>{color,idx})
                      .Where(color=>color=255)
                      .Select(pair=>pair.idx)
                      .ToList();

或者,使用迭代器功能:

IEnumerable<int> ColorIndexes(IEnumerable<Color32> colors,int color)
{
    int i=0;
    foreach(var c in colors)
    {
        if(c==color) yield return i;
        i++;
    }
}

... 

var leafIndexes=ColorIndexes(colors,255).ToList();

最终查询尝试将顶点成三份批处理。迭代器方法也可以在这里提供帮助,但是更好的主意是使用MoreLINQ's Batch运算符:

int[] vertices= mesh.triangles;
var triangles=vertices.Batch(3);

此后,查询将尝试查找leafIndexes列表中包含哪些“三角形”值。每个“三角形”都是数字列表。我们可以这样写:

var finalTriangles=triangles.Where(points=> points.Any(point=>leafIndexes.Contains(point));

试图查找叶子索引中是否包含任何三角形点。或者我们可以使用Enumerable.Intersect来查看两个数组是否有任何共同的值:

var finalTriangles=vertices.Batch(3)
                           .Where(points=> points.Intersect(leafIndexes).Any());

查询的最后一步将创建一个字典,该字典具有匹配的“三角形”,其关键字为“三角形”索引。同样,这是Select((item,index))的工作:

int[] vertices= mesh.triangles;
var finalTriangles=vertices.Batch(3)
                           .Select((triplet,idx)=>{ triplet=triplet.ToList(),
                                                    idx})
                           .Where(pair=> pair.triplet.Intersect(leafIndexes).Any())
                           .ToDictionary(pair=>pair.idx,
                                         pair=>pair.triplet);

代码仅使用ToList()执行一次可枚举,然后返回使用List . Without it, every time triplet or leafIndexes`,查询将再次执行。

此代码所做的一件事是将这些点放入HashSet中。此类适用于基于集合的快速就地操作。如果triplet是HashSet,则调用IntersectWith会对其进行修改,只保留在leafIndexes中找到的数字。

答案 1 :(得分:0)

我不是C#专家,但正如我在Microsoft文档的示例中所见

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.select?view=netframework-4.8

Select有对象及其索引,因此VERT只是三角形元素之一,而INDEX就是它的索引。我的意思是它是数组中的索引,例如三角形数组[Triangle1,Triangle2,Triangle3]的(object,index)对为(Triangle1,0),(Triangle2,1),(Triangle3,2)

答案 2 :(得分:0)

  

int类型如何具有称为vert的属性?而且int和Color32都没有属性索引。

您在这里感到困惑:这些不是“属性”,而是迭代变量。

Linq基本上一直都是

的快捷方式
foreach(var color in colors)

例如。如果您将其称为coloritem或其他任何东西,完全取决于您!

老实说,我不知道,但是显然index可以在需要时包括在内。它是您在使用Linq的列表/ IEnumerable中相应元素的索引。

因此,在GroupBy中,您实际上可以再次将其称为color而不是c

在以后的vert中也不是属性,而只是当前项目的名称..您也可以使用

Dictionary<int, HashSet<int>> faces = triangles
    .Select((v, i) => new { v, i })

它会做完全一样的事情。