简体   繁体   English

如何将Linq扩展方法重写为委托?

[英]How can I rewrite Linq Extension Methods as delegates?

I asked this question, How do I return one-to-many records in a specific order with Dapper and multi-mapping? 我问了这个问题, 如何使用Dapper和多重映射按特定顺序返回一对多记录?

and the answer is great, but I do not understand why it works. 答案是肯定的,但我不明白为什么会起作用。 I don't understand because I don't know how to read it. 我不懂,因为我不知道该怎么读。

How can I rewrite this as delegates? 如何将其重写为代表? I want to see the functions instead of the lambda, only so I can visualize it. 我只想查看函数而不是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();

I am mostly interested in this portion: 我对这部分最感兴趣:

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

and its relationship to 及其与

(k, v)

After that I can get it, I believe. 在那之后,我相信了。

You mean you want to see something like this: 您的意思是您想看到以下内容:

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);

Important is: fId returns int . 重要的是:fId返回int And fPosts returns Foo . fPosts返回Foo Because of this, parameters of the fSelect are int and IEnumerable<Foo> . 因此,fSelect的参数为intIEnumerable<Foo>

GroupBy is a extension method with several overloads here more about that. GroupBy是一种扩展方法, 这里有一些重载。 The overload that is used your example has the following reading. 您的示例使用的重载具有以下内容。

The first argument ( f => f.UserId ) is a so called key selector - this will determ the key for each group. 第一个参数( f => f.UserId是所谓的键选择器-这将确定每个组的键。

The second argument( posts => posts ) is the value selector - this will determ what type of values will be contained in the groups 第二个参数( posts => posts )是值选择器-这将确定组中将包含哪种类型的值

The third argument( (k, v) ) is called the result selector gives you one additional step before the group is materialized. 第三个参数( (k, v)称为结果选择器,使您在实现该组之前可以再执行一步。 This you can access the key ( UserId ) as first argument and a collection values (the values that will be contained in the group - all Posts for the UserId ) this gives you the ability to change/filter/project the result aditionally. 这样,您可以访问UserId )作为第一个参数和一个集合值 (该值将包含在组中UserId所有Posts ),这使您能够随意更改/过滤/投影结果。

So for example if the third argument was omitted then the result will be group where the key is the UserId and the values in the Groups are the Posts , but with the third argument we can now access the UserId(the key) and the Posts(the values) and project them into a new type - User . 因此,例如,如果省略了第三个参数,那么结果将是group,其中键是UserId ,而Groups中的值是Posts ,但是有了第三个参数,我们现在可以访问UserId(键)和Posts(值)并将其投影到新的类型User

Read your answer as: 阅读答案为:

Group userposts by UserId; 按UserId分组用户帖子; for each group, take all the posts for that id; 对于每个群组,请获取该ID的所有帖子; now for each group, take the userId and all its posts and do ... with them. 现在,对于每个组,获取userId及其所有帖子,并对其进行处理。

Explanation 说明

The expressions f => f.UserId , posts => posts look as if they're functions. 表达式f => f.UserIdposts => posts看起来像是函数。 But they're not. 但事实并非如此。 Well, they are, but kind of not ... well, this is confusing. 好吧,它们是,但是有点……不好,这令人困惑。 What they are doing is filling in for some syntax that can't exist in the C# language. 他们正在做的事情是填写C#语言中不存在的某些语法。

The point is, how can you communicate to the GroupBy() method: 关键是,如何与GroupBy()方法进行通信:

  • What you want to group by: the key 您要分组的内容:密钥
  • What you want to have in each group: The elements 您希望在每个小组中拥有什么:元素
  • What (if any) further processing you want to do with the elements in each group. 您希望对每个组中的元素进行哪些进一步的处理(如果有)。 (This third bullet is a nice-to-have, but that's what the framework gives us) (第三个项目符号是很不错的,但这就是框架为我们提供的功能)

It might be nice if C# could support this syntax: 如果C#支持以下语法可能会很好:

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

but you can see at once that could never compile. 但是您可以立即看到它永远无法编译。 You have to explain to the compiler that that UserId means the property of a UserPost of that name and that this is scoped to each userpost in turn. 你必须向他解释说,编译器UserId是指财产UserPost那个名字,并且this范围限定为依次在每个userpost。 Before generics & lambdas, .net often solved this kind of thing by using strings for the fieldname: 在泛型和lambdas之前,.net通常通过使用字符串作为字段名来解决此类问题:

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

and then there would have to be some runtime reflection to turn that string into a propertyname & then get a value, & to realise that "" means the whole UserPost 然后必须进行一些运行时反射,才能将该字符串转换为属性名称,然后获取一个值,并意识到“”表示整个UserPost

But in C# we love strong-typing. 但是在C#中,我们喜欢强类型。 And the lambda gives a cute-but-not-instantly-obvious way to "name" a property in a strong typed way: 而且,lambda提供了一种可爱但并非立即显而易见的方式来以强类型方式“命名”属性:

f=> f.UserId

you see? 你看? Read this not so much as a function but as naming the property: "I want the UserId of each userpost" 阅读此书不只是作为一个函数,而是为该属性命名:“我想要每个userpost的UserId”

Similarly, you can 'name' the whole entity: 同样,您可以“命名”整个实体:

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

(The function x=>x is called the identity function in computer science). (函数x=>x在计算机科学中称为身份函数)。 And this is why it's hard to 'visualise' the delegate. 这就是为什么很难“可视化”代表的原因。 The lambda is being used as a neat, strongly-typed way to identify a field or property. lambda被用作标识字段或属性的简洁, 强类型的方式。

[If that's crystal clear, let me now retract the 'it's not a function' statement. [如果很清楚,让我现在撤消“这不是函数”的声明。 It is a function. 它是一个功能。 Suppose you want to group by, not the UserId but by the name, and you want the grouping to be case-insensitive. 假设您要按名称不是UserId分组,而要按名称分组, 并且希望分组不区分大小写。 Then you could use: 然后,您可以使用:

f=> f.Name.ToUpper()

So being a function is not just a way to get strong typing, it also gives more powerful options. 因此,成为一个函数不仅是获得强类型的方法,它还提供了更强大的选择。 You can do this in SQL too. 您也可以在SQL中执行此操作。 ] ]

The final bit: 最后一点:

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

you then read as "for each userId & and set of posts for that userId, do ... with them". 然后,您将其读为“对于每个userId&,以及该userId的帖子集,请对它们进行...”。 Notice that the names k & v are arbitrary parameter names and not so helpful here. 请注意,名称k和v是任意参数名称,在这里并不是很有用。 I might have used: 我可能用过:

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

Bringing it all together, you read 汇集全部,您阅读

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

aloud as: 大声说:

Group userposts by UserId; 按UserId分组用户帖子; for each group, take all the posts for that id; 对于每个群组,请获取该ID的所有帖子; now for each group, take the userId and all its posts and do ... with them 现在,对于每个组,获取userId及其所有帖子,并对其进行...

——————————————————————————————————— ————————————————————————————————————

So, you see, seeing the delegate doesn't help much: 因此,您看到,看到委托并没有太大帮助:

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; } }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM