如何将Linq扩展方法重写为代理?

时间:2017-01-28 19:26:15

标签: c# .net linq lambda delegates

我问了这个问题,How do I return one-to-many records in a specific order with Dapper and multi-mapping?

答案很好,但我不明白为什么会这样。我不明白,因为我不知道如何阅读它。

如何将其重写为代表?我想看看函数而不是lambda,只是因此我可以想象它。

var groupedPosts = userposts.GroupBy(f => f.UserId, posts => posts, (k, v) =>
    new User()
    {
        UserId = k,
        Name = v.FirstOrDefault().Name,
        Posts = v.Select(f => new Post() { Id = f.Id, 
                                           Title= f.Title, 
                                           Content = f.Content})
    }).ToList();

我最感兴趣的是这部分:

f => f.UserId, posts => posts,

及其与

的关系
(k, v)

之后,我相信,我可以得到它。

3 个答案:

答案 0 :(得分:2)

你的意思是你想要看到这样的东西:

public static int fId(Foo f)
{
    return f.Id;
}

public static Foo fPosts(Foo f)
{
    return f;
}

public static User fSelect(int k, IEnumerable<Foo> v )
{
    return new User()
    {
        UserId = k,
        Name = v.FirstOrDefault().Name,
        Posts = v.Select(f => new Post()
        {
            Id = f.Id,
            Title = f.Title,
            Content = f.Content
         })
    };
}


var groupedPosts = userposts.GroupBy(fId, fPosts, fSelect);

重要的是:fId返回int。并且fPosts返回Foo。因此,fSelect的参数为intIEnumerable<Foo>

答案 1 :(得分:2)

GroupBy是一种扩展方法,有多个重载here。 您的示例使用的重载具有以下读数。

第一个参数(f => f.UserId是一个所谓的密钥选择器 - 这将确定每个组的密钥。

第二个参数(posts => posts )是值选择器 - 这将确定组中包含的值的类型

调用第三个参数((k, v),结果选择器会在实现组之前为您提供一个额外的步骤。您可以访问UserId)作为第一个参数和集合值(将包含在组中的值 - 所有{{1}对于Posts),这使您能够更改/过滤/预测结果。

因此,例如,如果第三个参数被省略,那么结果将是组,其中键是UserId,组中的值是UserId,但是我们现在可以使用第三个参数访问UserId(密钥)和帖子(值)并将它们投影到新类型 - Posts

答案 2 :(得分:1)

阅读你的答案:

  

按UserId分组用户点;对于每个组,请获取该ID的所有帖子;现在,对于每个组,请使用userId及其所有帖子,并与他们一起做。

<强>解释

表达式f => f.UserIdposts => posts看起来好像是函数。但他们不是。嗯,他们是,但有点不......好吧,这很令人困惑。他们正在做的是填写一些C#语言中不存在的语法。

重点是,您如何与GroupBy()方法进行沟通:

  • 您想要分组的内容:密钥
  • 您希望在每个组中拥有什么:元素
  • 您希望对每个组中的元素进行进一步处理(如果有)。 (这第三个子弹是一个很好的,但这就是框架给我们的东西)

如果C#可以支持这种语法,那可能会很好:

var groupedPosts = userposts.GroupBy( UserId, this, (k, v) => {...});

但你可以立刻看到它永远无法编译。您必须向编译器解释UserId表示该名称的UserPost的属性,并且this依次限定每个用户邮件。在仿制药之前lambdas,.net经常通过使用字段名字符串来解决这类问题:

var groupedPosts = userposts.GroupBy( "UserId", "", (k, v) => {...});

然后必须有一些运行时反射将该字符串转换为属性名称&amp;然后得到一个值,&amp;认识到“”意味着整个UserPost

但是在C#中我们喜欢强力打字。 lambda给出了一种可爱但不是立即显而易见的方式,以强有力的方式“命名”一个属性:

f=> f.UserId
你知道吗?阅读这不是一个功能,而是命名属性:“我想要每个用户信息的UserId”

同样,您可以“命名”整个实体:

f => f // means exactly the same as posts=>posts

(函数x=>x在计算机科学中称为身份函数)。这就是为什么很难“想象”代表。 lambda被用作一种简洁的强类型方式来识别字段或属性。

[如果这很清楚,现在让我收回'这不是一个功能'的陈述。这是一个功能。假设您要分组,而不是UserId,而是名称​​和,您希望分组不区分大小写。然后你可以使用:

f=> f.Name.ToUpper()

因此,作为一个功能不仅仅是一种获得强类型的方法,它还提供了更强大的选项。您也可以在SQL中执行此操作。 ]

最后一点:

(k, v) => {....}

然后你读作“为每个userId&amp;和该用户ID的帖子集,做......与他们一起”。请注意名称k&amp; v是任意参数名称,在这里没有那么有用。我可能用过:

(userid, postsforuserid) => {...}

将所有内容整合在一起,您可以阅读

    userposts.GroupBy(
       userpost  =>  userpost.UserId, 
       userpost  =>  userpost,
       (userid, postsforuserid) => {...})

大声说:

  

按UserId分组用户点;对于每个组,请获取该ID的所有帖子;现在,对于每个组,请使用userId及其所有帖子并使用它们执行

-----------------------------------

所以,你看,看到代表没有多大帮助:

void Main()
{
    Del k = delegate (UserPost post) { return post.UserId; };
    Del v = delegate (UserPost post) { return post; };
}
class UserId { }
class UserPost { public UserId UserId { get; set; } }