我真的很喜欢C#中的GroupBy LINQ方法。
我不喜欢的一件事是Key总是被称为Key
。
当我遍历群组时,Key
并没有告诉我任何事情。因此,我必须查看其余的上下文,以了解Key究竟是什么。
var grouped_by_competition = all_events
.GroupBy(e => e.Competition);
foreach ( var group in grouped_by_competition )
{
[...]
// ok what is Key for kind of object? I have to look at the outer loop to understand
group.Key.DoSomething();
}
当然你可以说它是一件小事,因为你只需要看外环。
在我看来,当你阅读它时,它仍会吃掉“大脑”。因此,如果有很好的替代品需要很少的样板,我想使用它。
有一些替代方案,但它们都添加了样板代码或依赖于评论 - 我都试图省略。
选项1 - 循环内部的额外变量
var grouped_by_competition = all_events
.GroupBy(e => e.Competition)
foreach ( var group in grouped_by_competition )
{
var competition = group.Key;
[...]
competition.DoSomething();
}
此解决方案添加了不必要的样板代码。
选项2 - 额外选择
var grouped_by_competition = all_events
.GroupBy(e => e.Competition)
.Select(x => new { Competition = x.Key, Items = x } );
foreach ( var group in grouped_by_competition )
{
[...]
group.Competition.DoSomething();
}
此解决方案添加了不必要的样板代码。
选项3 - 添加关于使用密钥
的评论我宁愿使用富有表现力的代码,也要过时的评论。
摘要
理论上是否可以设计一种类似于匿名类功能的扩展方法?
var example = new { e.Competition };
example.Competition.DoSomething(); // property name deduced
所以我们得到:
var grouped_by_competition = all_events
.NamedGroupBy(e => e.Competition);
foreach ( var group in grouped_by_competition )
{
[...]
group.Competition.DoSomething();
}
答案 0 :(得分:2)
理论上是否可以设计一种类似于匿名类功能的扩展方法?
不,因为名称必须在编译时出现,并且您建议的扩展方法将依赖于在运行时检查的Expression
对象。
仅方式,您可以通过名称引用成员,如果被引用的对象实际上具有该名称的成员。编译器必须有一个实际的编译时名称。
(我说谎。当您使用dynamic
关键字时,您告诉编译器将编译时操作推迟到运行时。这样您就可以放弃使用#39; t解析具有特定成员名称的类型直到运行时。它实际上是一种满足你既定目标的方法。但是,在运行时运行编译器可能很昂贵,并放弃编译时类型安全性是C#和类似静态类型语言的标志性特征。dynamic
关键字在某些情况下是一个重要特征,但我不会说这个特定用例证明了成本。)
就个人而言,我找不到名称Key
的问题。在一个非常重要的方面,它的非常表达:也就是说,它告诉我我正在处理分组的关键。如果我想了解更多信息,它的类型通常足以进一步详细说明,并且可以通过将鼠标悬停在它上面立即获得。
但是,不同人的不同笔画。如果目标是让一个命名成员与Key
不同,那么您的匿名类型投影就是恕我直言,这是您最好的。请注意,您可以使用GroupBy()
重载来提高简洁性,该重载允许您直接投影每个组,而不是使用Select()
方法:
var grouped_by_competition = all_events
.GroupBy(e => e.Competition, (k, g) => new { Competition = k, Items = g });
所以,另外一件事:当我写了" only 方式让你能够通过名字引用成员时,如果被引用的对象实际上有一个那个名字的成员",那里有点挥手。也就是说,编译器在编译时知道名称是非常重要的。
通常,这意味着确实存在具有该名称的命名成员的类型。但该规则有一个例外,最近添加到C#,这是使用ValueTuple
的新元组语法。有了它,您可以隐式地给出一个名称而不引入实际的新类型。编译器只是在本地方法中跟踪它,并将其视为实际类型。
在此特定方案中,元组语法并未真正改进匿名类型语法。它有点短,但不是很多:
var grouped_by_competition = all_events
.GroupBy(e => e.Competition, (k, g) => (Competition: k, Items: g));
使用ValueTuple
类确实具有潜在的性能优势;作为一种值类型,使用它可以显着降低GC开销。但并非所有场景都会从中受益。
答案 1 :(得分:1)
如果你可以进入dynamic
的痛苦世界,那么你可以实现它。我不推荐它。
class DynamicGroup<TKey, TElement> : DynamicObject, IGrouping<TKey, TElement> {
private string keyname;
private IGrouping<TKey, TElement> items;
public DynamicGroup(IGrouping<TKey, TElement> pItems, string pKeyname) {
items = pItems;
keyname = pKeyname;
}
public IEnumerator<TElement> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public TKey Key { get => items.Key; }
public override bool TryGetMember(GetMemberBinder binder, out object result) {
if (binder.Name == keyname) {
result = items.Key;
return true;
}
else {
result = null;
return false;
}
}
}
static class Ext {
public static IEnumerable<dynamic> NamedGroupBy<TElement, TKey>(this IEnumerable<TElement> src, Expression<Func<TElement, TKey>> keySelector) {
var ksb = keySelector.Body as MemberExpression;
return src.GroupBy(keySelector.Compile()).Select(g => new DynamicGroup<TKey, TElement>(g, ksb.Member.Name));
}
}
答案 2 :(得分:0)
我不确定哪种代码适合您,但是如何在Select方法中使用动态类型?
var grouped_by_competition = all_events
.GroupBy(e => e.Competition)
.Select(x => {{
dynamic ret=new System.Dynamic.ExpandoObject();
ret.Competition=x.Key;
ret.Items=x;
return ret;
} );
foreach( var group in grouped_by_competition )
{
group.Competition.DoSomething();
}