Is it possible to combine GroupBy and Select to get the correct named key?

I really like the GroupBy LINQ method in C #.

One thing that I don't like is that the key is always called Key .

When I browse through groups, Key doesn't tell me anything. So I have to look at the rest of the context to understand what Key really is.

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

Of course, you could argue that this is a trifle, because you only need to look at the outer loop.

In my opinion, he still eats "braincycles" when you read it. Therefore, if there are good alternatives that need a small template, I would like to use it.

There are several alternatives, but they all add boilerplate code or are dependent on comments - and I try to skip.

Option 1 - Additional variable inside the loop

 var grouped_by_competition = all_events .GroupBy(e => e.Competition) foreach ( var group in grouped_by_competition ) { var competition = group.Key; [...] competition.DoSomething(); } 

This solution adds unnecessary boilerplate code.

Option 2 - Extra Select

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

This solution adds unnecessary boilerplate code.

Option 3 - Add a comment next to using the key

I rather have expressive code, and then the comments are deprecated.

Summary

Is it theoretically possible to develop an extension method that somehow works similar to an anonymous function class?

 var example = new { e.Competition }; example.Competition.DoSomething(); // property name deduced 

So, we get:

 var grouped_by_competition = all_events .NamedGroupBy(e => e.Competition); foreach ( var group in grouped_by_competition ) { [...] group.Competition.DoSomething(); } 
+5
source share
3 answers

Is it theoretically possible to develop an extension method that somehow works similar to an anonymous function class?

No, because the name must be present at compile time, and the proposed extension method will rely on the Expression object to be checked at runtime.

The only way you could refer to a member by name would be if the object referenced actually has a member of that name. For the compiler, there must be an actual compile time name.

(I lie. When you use the dynamic keyword, you tell the compiler to postpone compilation operations at runtime. This allows you to avoid tricks that do not allow types with a specific member name until they are executed at runtime. This is on actually one way to achieve this goal, however, running the compiler at runtime is potentially expensive and relinquishes compilation-type security, which is a hallmark of C # and similar statically -tped The dynamic keyword is an important feature in some scenarios, but I would not say that this particular use case is worth the cost.)

Personally, I do not find the name Key problem. In one very important respect, this is very expressive: that is, he tells me that I am dealing with a grouping key. If I want to know more about this, its type is usually sufficient for further development, and it is immediately available, hovering over it.

But different strokes for different people. If the goal is to make the named member different from Key , your anonymous IMHO type projection is the best you are going to get. Note that you can improve brevity with GroupBy() overload, which allows you to project each group directly, instead of the Select() method:

 var grouped_by_competition = all_events .GroupBy(e => e.Competition, (k, g) => new { Competition = k, Items = g }); 

So, one more thing: when I wrote that "the only way for you to be able to refer to a member by name will be if there is actually a member of that name on the object to which there is a link", there is a little manual waving there. That is, what matters is that the compiler knows about the name at compile time.

This usually means that there really is a type with a named member having that name. But there is an exception to this rule recently added in C #, which is the new tuple syntax using ValueTuple . However, you can implicitly specify a name without introducing a new new type. The compiler simply tracks it in the local method and processes it as if it were relevant.

The tuple syntax does not actually improve the syntax of the anonymous type in this particular scenario. This is a little shorter, but not much:

 var grouped_by_competition = all_events .GroupBy(e => e.Competition, (k, g) => (Competition: k, Items: g)); 

This has the potential performance advantage of using the ValueTuple class; being a type of value, there can be a significant reduction in GC overhead by using it. But not all scenarios will benefit from this.

+2
source

If you're fine, entering the painful dynamic world, you can implement it. I would not recommend it.

 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)); } } 
+1
source

I'm not sure which codes are clean for you, but how to use the dynamic type in the Select method?

 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(); } 
0
source

Source: https://habr.com/ru/post/1269999/


All Articles